├── .babelrc
├── .gitignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── src
├── App.vue
├── components
│ ├── List.vue
│ ├── Message.vue
│ └── MessageToggle.vue
└── main.js
├── test
├── List.spec.js
├── Message.spec.js
├── MessageToggle.spec.js
└── setup.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "development": {
4 | "presets": [
5 | ["env", { "modules": false }]
6 | ]
7 | },
8 | "test": {
9 | "presets": [
10 | ["env", {
11 | "modules": false,
12 | "targets": { "node": "current" }
13 | }]
14 | ],
15 | "plugins": ["istanbul"]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log
5 | yarn-error.log
6 | .tmp
7 | .nyc_output
8 | coverage
9 |
10 | # Editor directories and files
11 | .idea
12 | *.suo
13 | *.ntvs*
14 | *.njsproj
15 | *.sln
16 | *.sw[po]
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # THIS REPOSITORY IS DEPRECATED
2 |
3 | > Note: This example is outdated. It's now recommended to scaffold your project with [Vue CLI 3](https://cli.vuejs.org/) which provides out-of-the-box configurations for unit testing.
4 |
5 | # vue-test-utils-mocha-example
6 |
7 | > Example project using mocha-webpack and vue-test-utils
8 |
9 | This is based on the `vue-cli` `webpack-simple` template. Test-specific changes include:
10 |
11 | ### Additional Dependencies
12 |
13 | - `vue-test-utils`
14 | - `mocha` & `mocha-webpack`
15 | - `jsdom` & `jsdom-global` (for setting up DOM environment in tests)
16 | - `webpack-node-externals` (for excluding NPM deps from test bundle)
17 | - `expect` (for assertions)
18 | - This is the package used internally by Jest, so [usage is the same](http://facebook.github.io/jest/docs/en/expect.html#content). You can also use [chai](http://chaijs.com/) + [sinon](http://sinonjs.org/).
19 | - `nyc` & `babel-plugin-istanbul` (for coverage)
20 |
21 | ### Additional Configuration
22 |
23 | #### `package.json`
24 |
25 | Added `test` script and setting for `nyc`:
26 |
27 | ``` js
28 | {
29 | // ...
30 | "scripts": {
31 | // ...
32 | "test": "cross-env NODE_ENV=test nyc mocha-webpack --webpack-config webpack.config.js --require test/setup.js test/**/*.spec.js"
33 | },
34 | "nyc": {
35 | "include": [
36 | "src/**/*.(js|vue)"
37 | ],
38 | "instrument": false,
39 | "sourceMap": false
40 | }
41 | }
42 | ```
43 |
44 | #### `webpack.config.js`
45 |
46 | Added test-specific configs:
47 |
48 | ``` js
49 | if (process.env.NODE_ENV === 'test') {
50 | // exclude NPM deps from test bundle
51 | module.exports.externals = [require('webpack-node-externals')()]
52 | // use inline source map so that it works with mocha-webpack
53 | module.exports.devtool = 'inline-cheap-module-source-map'
54 | }
55 | ```
56 |
57 | #### `test/setup.js`
58 |
59 | Global setup for tests. This is run first with `mocha-webpack`'s `--require` flag.
60 |
61 | ``` js
62 | // setup JSDOM
63 | require('jsdom-global')()
64 |
65 | // make expect available globally
66 | global.expect = require('expect')
67 | ```
68 |
69 | #### `.babelrc`
70 |
71 | Added `"plugins": ["istanbul"]`:
72 |
73 | ```js
74 | {
75 | "env": {
76 | // ...
77 | "test": {
78 | "plugins": ["istanbul"]
79 | }
80 | }
81 | }
82 | ```
83 |
84 | ## Build Setup
85 |
86 | ``` bash
87 | # install dependencies
88 | npm install
89 |
90 | # serve with hot reload at localhost:8080
91 | npm run dev
92 |
93 | # build for production with minification
94 | npm run build
95 |
96 | # run unit tests
97 | npm test
98 | ```
99 |
100 | For detailed explanation on how things work, consult the [docs for vue-test-utils](https://vue-test-utils.vuejs.org/guides/#testing-single-file-components-with-mocha-webpack).
101 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | vue-test-utils-mocha-example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-test-utils-mocha-example",
3 | "description": "Example project using mocha-webpack and vue-test-utils",
4 | "version": "1.0.0",
5 | "author": "Evan You ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
9 | "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
10 | "test": "cross-env NODE_ENV=test nyc --reporter=lcov --reporter=text mocha-webpack --webpack-config webpack.config.js --require test/setup.js test/**/*.spec.js"
11 | },
12 | "dependencies": {
13 | "vue": "^2.4.4"
14 | },
15 | "devDependencies": {
16 | "@vue/test-utils": "^1.0.0-beta.25",
17 | "babel-core": "^6.26.3",
18 | "babel-loader": "^7.1.2",
19 | "babel-plugin-istanbul": "^4.1.5",
20 | "babel-preset-env": "^1.7.0",
21 | "cross-env": "^5.0.5",
22 | "css-loader": "^0.28.7",
23 | "expect": "^21.2.1",
24 | "file-loader": "^1.1.4",
25 | "jsdom": "^12.1.0",
26 | "jsdom-global": "^3.0.2",
27 | "mocha": "^5.2.0",
28 | "mocha-webpack": "^1.1.0",
29 | "nyc": "^12.0.2",
30 | "vue-loader": "^12.2.2",
31 | "vue-template-compiler": "^2.4.4",
32 | "webpack": "^3.6.0",
33 | "webpack-dev-server": "^2.9.1",
34 | "webpack-node-externals": "^1.6.0"
35 | },
36 | "nyc": {
37 | "include": [
38 | "src/**/*.(js|vue)"
39 | ],
40 | "instrument": false,
41 | "sourceMap": false
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
19 |
20 |
26 |
--------------------------------------------------------------------------------
/src/components/List.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | {{ item }}
5 |
6 |
7 |
8 |
9 |
17 |
--------------------------------------------------------------------------------
/src/components/Message.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ msg || 'default message' }}
3 |
4 |
5 |
13 |
--------------------------------------------------------------------------------
/src/components/MessageToggle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
10 |
28 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 |
4 | new Vue({
5 | el: '#app',
6 | render: h => h(App)
7 | })
8 |
--------------------------------------------------------------------------------
/test/List.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import List from '@/components/List.vue'
3 |
4 | describe('List.vue', () => {
5 | it('renders li for each item in props.items', () => {
6 | const items = ['', '']
7 | const wrapper = shallowMount(List, {
8 | propsData: { items }
9 | })
10 | expect(wrapper.findAll('li')).toHaveLength(items.length)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/test/Message.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import Message from '@/components/Message.vue'
3 |
4 | describe('Message.vue', () => {
5 | it('renders props.msg when passed', () => {
6 | const msg = 'new message'
7 | const wrapper = shallowMount(Message, {
8 | propsData: { msg }
9 | })
10 | expect(wrapper.text()).toBe(msg)
11 | })
12 |
13 | it('renders default message if not passed a prop', () => {
14 | const defaultMessage = 'default message'
15 | const wrapper = shallowMount(Message)
16 | expect(wrapper.text()).toBe(defaultMessage)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/test/MessageToggle.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils'
2 | import MessageToggle from '@/components/MessageToggle.vue'
3 | import Message from '@/components/Message.vue'
4 |
5 | describe('MessageToggle.vue', () => {
6 | it('toggles msg passed to Message when button is clicked', () => {
7 | const wrapper = shallowMount(MessageToggle)
8 | const button = wrapper.find('#toggle-message')
9 | button.trigger('click')
10 | const MessageComponent = wrapper.find(Message)
11 | expect(MessageComponent.props()).toEqual({msg: 'message'})
12 | button.trigger('click')
13 | expect(MessageComponent.props()).toEqual({msg: 'toggled message'})
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | // setup JSDOM
2 | require('jsdom-global')()
3 |
4 | // make expect available globally
5 | global.expect = require('expect')
6 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var webpack = require('webpack')
3 |
4 | module.exports = {
5 | entry: './src/main.js',
6 | output: {
7 | path: path.resolve(__dirname, './dist'),
8 | publicPath: '/dist/',
9 | filename: 'build.js'
10 | },
11 | resolve: {
12 | alias: {
13 | 'vue$': 'vue/dist/vue.esm.js',
14 | '@': path.resolve(__dirname, 'src')
15 | }
16 | },
17 | module: {
18 | rules: [
19 | {
20 | test: /\.vue$/,
21 | loader: 'vue-loader'
22 | },
23 | {
24 | test: /\.js$/,
25 | loader: 'babel-loader',
26 | exclude: /node_modules/
27 | },
28 | {
29 | test: /\.(png|jpg|gif|svg)$/,
30 | loader: 'file-loader',
31 | options: {
32 | name: '[name].[ext]?[hash]'
33 | }
34 | }
35 | ]
36 | },
37 | devServer: {
38 | historyApiFallback: true,
39 | noInfo: true
40 | },
41 | performance: {
42 | hints: false
43 | },
44 | devtool: '#eval-source-map'
45 | }
46 |
47 | if (process.env.NODE_ENV === 'production') {
48 | module.exports.devtool = '#source-map'
49 | module.exports.plugins = (module.exports.plugins || []).concat([
50 | new webpack.DefinePlugin({
51 | 'process.env': {
52 | NODE_ENV: '"production"'
53 | }
54 | }),
55 | new webpack.optimize.UglifyJsPlugin({
56 | sourceMap: true,
57 | compress: {
58 | warnings: false
59 | }
60 | }),
61 | new webpack.LoaderOptionsPlugin({
62 | minimize: true
63 | })
64 | ])
65 | }
66 |
67 | // test specific setups
68 | if (process.env.NODE_ENV === 'test') {
69 | module.exports.externals = [require('webpack-node-externals')()]
70 | module.exports.devtool = 'eval'
71 | }
72 |
--------------------------------------------------------------------------------