Vue Component Tests

Setup

First, install the necessary packages:

npm install --save-dev @shutter/vue vue

You will need to authenticate to the shutter service in order to use it. Check out the section in the Getting Started guide if you haven't yet.

Write a test

Create a new test file for a Vue component of yours. There are no hard restrictions about how to name and where to put the test file. Just put it where you usually place your component tests and make sure your test runner (Jest, Mocha, ...) will find it.

Here is an example testing a MyButton component, using good old Mocha as the test runner.

// src/components/MyButton.shutter.js
import createVueShutter from '@shutter/vue'
import MyButton from './MyButton'
const shutter = createVueShutter(__dirname)
describe('Button component', function () {
after(async function () {
// Collect and evaluate results once we are done
await shutter.finish()
})
it('matches visual snapshot', async function () {
await Promise.all([
shutter.snapshot('Default Button', {
components: { MyButton },
template: '<my-button label='Click me'></my-button>'
}),
shutter.snapshot('Primary Button', {
components: { MyButton },
template: '<my-button primary label='Click me'></my-button>'
})
])
})
})

Run the test

Let's now run the tests for the first time and make sure they complete successfully. The first test run should always succeed, since there are no snapshots to compare to yet.

$ npx mocha

The first test run will create a snapshots directory and save the snapshots there as PNG images.

When running this test again, @shutter/vue will once more render your button and compare the resulting snapshots to your previous component snapshots. The tests will fail if the snapshots don't match, printing a list of the failed test cases and an inspection link.

The inspection link references the shutter.sh web app where you can see the rendered components, the expected outcomes and a visual diff between them.

CLI & Updating snapshots

First make sure the shutter package is installed:

$ npm install --save-dev shutter

Change your component, so that the new snapshot won't match the old one anymore. Run the tests and you will see an overview of the failed test cases.

What if this change was intentional? You want to update your locally stored snapshot(s), so the tests succeed again and future test runs will compare to the new visual appearance.

Let's update the snapshots using the shutter command line tool:

# `npx` comes with npm > 5.2 and will run shutter from ./node_modules/.bin/shutter
$ npx shutter update

You will see an interactive prompt that allows you to select the snapshots you want to update. Select them with Space and confirm with Enter, that's it.

Shutter CLI in action

If you need detailed usage information for the shutter command line tool, just run npx shutter --help.

Custom <head>

The <head> section of the HTML document is easily customizable.

import createVueShutter from '@shutter/vue'
const head = `
<link href="/styles.css" rel="stylesheet" />
`
const shutter = createVueShutter(__dirname, { head })

Submit local files

You can submit local files that will be served on a path of your choice while rendering. This way you can use custom stylesheets, for instance.

import createVueShutter, { addFile } from '@shutter/vue'
import * as path from 'path'
const files = await Promise.all([
addFile(path.join(__dirname, 'styles.css'), '/styles.css')
])
const head = `
<link href="/styles.css" rel="stylesheet" />
`
const shutter = createVueShutter(__dirname, { files, head })

Please note that the submitted file will be publicly accessible.

Using AVA

The test runner AVA is quite popular for its lean and clean test API. You can easily write tests using shutter as well:

import createVueShutter from '@shutter/vue'
import MyButton from './MyButton'
const shutter = createVueShutter(__dirname)
test.after(async () => {
// Collect and evaluate results once we are done
await shutter.finish()
})
test('Button', async t => {
await Promise.all([
shutter.snapshot('Default Button', {
components: { MyButton },
template: '<my-button label='Click me'></my-button>'
}),
shutter.snapshot('Primary Button', {
components: { MyButton },
template: '<my-button primary label='Click me'></my-button>'
})
])
t.pass()
})

This is an example test using Vuetify components and AVA as the test runner.

import test from 'ava'
import Vue from 'vue'
import Vuetify from 'vuetify'
import createVueShutter, { addFile } from '../src'
test('Vuetify components', async t => {
Vue.use(Vuetify)
const files = await Promise.all([
addFile(require.resolve('vuetify/dist/vuetify.css'), '/vuetify.css')
])
const head = `
<link href="/vuetify.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons" rel="stylesheet" />
`
const shutter = createVueShutter(__dirname, { files, head })
await shutter.snapshot('Vuetify button', {
template: `<v-app>
<v-btn>I am a button</v-btn>
</v-app>`
})
await shutter.snapshot('Vuetify toolbar with content', {
template: `<v-app>
<v-toolbar>
<v-toolbar-side-icon></v-toolbar-side-icon>
<v-toolbar-title>Title</v-toolbar-title>
<v-spacer></v-spacer>
<v-toolbar-items class="hidden-sm-and-down">
<v-btn flat>Link One</v-btn>
<v-btn flat>Link Two</v-btn>
<v-btn flat>Link Three</v-btn>
</v-toolbar-items>
</v-toolbar>
</v-app>`
})
await shutter.finish()
t.pass()
})