DEV Community

Jay
Jay

Posted on

2 1

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

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay