├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── package.json ├── src └── vue-jest-utils.ts ├── test ├── src │ ├── counter-js.vue │ ├── counter-ts.ts │ └── counter-ts.vue └── test │ ├── __snapshots__ │ ├── counter-js.spec.js.snap │ └── counter-ts.spec.ts.snap │ ├── counter-js.spec.js │ └── counter-ts.spec.ts ├── tsconfig.json ├── tsconfigDist.json ├── tslint.json └── types └── html2jade.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | 3 | # Created by https://www.gitignore.io/api/macos,windows,linux,visualstudiocode,webstorm,sublimetext,node,git 4 | 5 | ### macOS ### 6 | *.DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | 34 | ### Windows ### 35 | # Windows image file caches 36 | Thumbs.db 37 | ehthumbs.db 38 | 39 | # Folder config file 40 | Desktop.ini 41 | 42 | # Recycle Bin used on file shares 43 | $RECYCLE.BIN/ 44 | 45 | # Windows Installer files 46 | *.cab 47 | *.msi 48 | *.msm 49 | *.msp 50 | 51 | # Windows shortcuts 52 | *.lnk 53 | 54 | 55 | ### Linux ### 56 | *~ 57 | 58 | # temporary files which can be created if a process still has a handle open of a deleted file 59 | .fuse_hidden* 60 | 61 | # KDE directory preferences 62 | .directory 63 | 64 | # Linux trash folder which might appear on any partition or disk 65 | .Trash-* 66 | 67 | 68 | ### VisualStudioCode ### 69 | .vscode/* 70 | !.vscode/settings.json 71 | !.vscode/tasks.json 72 | !.vscode/launch.json 73 | 74 | 75 | ### WebStorm ### 76 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 77 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 78 | 79 | # User-specific stuff: 80 | .idea/workspace.xml 81 | .idea/tasks.xml 82 | .idea/dictionaries 83 | .idea/vcs.xml 84 | .idea/jsLibraryMappings.xml 85 | 86 | # Sensitive or high-churn files: 87 | .idea/dataSources.ids 88 | .idea/dataSources.xml 89 | .idea/dataSources.local.xml 90 | .idea/sqlDataSources.xml 91 | .idea/dynamic.xml 92 | .idea/uiDesigner.xml 93 | 94 | # Gradle: 95 | .idea/gradle.xml 96 | .idea/libraries 97 | 98 | # Mongo Explorer plugin: 99 | .idea/mongoSettings.xml 100 | 101 | ## File-based project format: 102 | *.iws 103 | 104 | ## Plugin-specific files: 105 | 106 | # IntelliJ 107 | /out/ 108 | 109 | # mpeltonen/sbt-idea plugin 110 | .idea_modules/ 111 | 112 | # JIRA plugin 113 | atlassian-ide-plugin.xml 114 | 115 | # Crashlytics plugin (for Android Studio and IntelliJ) 116 | com_crashlytics_export_strings.xml 117 | crashlytics.properties 118 | crashlytics-build.properties 119 | fabric.properties 120 | 121 | ### WebStorm Patch ### 122 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 123 | 124 | # *.iml 125 | # modules.xml 126 | # .idea/misc.xml 127 | # *.ipr 128 | 129 | 130 | ### SublimeText ### 131 | # cache files for sublime text 132 | *.tmlanguage.cache 133 | *.tmPreferences.cache 134 | *.stTheme.cache 135 | 136 | # workspace files are user-specific 137 | *.sublime-workspace 138 | 139 | # project files should be checked into the repository, unless a significant 140 | # proportion of contributors will probably not be using SublimeText 141 | # *.sublime-project 142 | 143 | # sftp configuration file 144 | sftp-config.json 145 | 146 | # Package control specific files 147 | Package Control.last-run 148 | Package Control.ca-list 149 | Package Control.ca-bundle 150 | Package Control.system-ca-bundle 151 | Package Control.cache/ 152 | Package Control.ca-certs/ 153 | bh_unicode_properties.cache 154 | 155 | # Sublime-github package stores a github token in this file 156 | # https://packagecontrol.io/packages/sublime-github 157 | GitHub.sublime-settings 158 | 159 | 160 | ### Node ### 161 | /.npmrc 162 | # Logs 163 | logs 164 | *.log 165 | npm-debug.log* 166 | 167 | # Runtime data 168 | pids 169 | *.pid 170 | *.seed 171 | *.pid.lock 172 | 173 | # Directory for instrumented libs generated by jscoverage/JSCover 174 | lib-cov 175 | 176 | # Coverage directory used by tools like istanbul 177 | coverage 178 | 179 | # nyc test coverage 180 | .nyc_output 181 | 182 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 183 | .grunt 184 | 185 | # node-waf configuration 186 | .lock-wscript 187 | 188 | # Compiled binary addons (http://nodejs.org/api/addons.html) 189 | build/Release 190 | 191 | # Dependency directories 192 | node_modules 193 | jspm_packages 194 | 195 | # Optional npm cache directory 196 | .npm 197 | 198 | # Optional REPL history 199 | .node_repl_history 200 | 201 | 202 | ### Git ### 203 | *.orig 204 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | script: npm run prepublish 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Locoslab GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-jest-utils [![Build Status](https://travis-ci.org/locoslab/vue-jest-utils.svg?branch=master)](https://travis-ci.org/locoslab/vue-jest-utils) [![npm version](https://badge.fury.io/js/vue-jest-utils.svg)](https://badge.fury.io/js/vue-jest-utils) 2 | Utilities for testing [Vue.js](http://vuejs.org/) components using [Jest](https://facebook.github.io/jest/). 3 | 4 | While written in TypeScript (and thus including type definitions), it can also be used in a pure JavaScript environment. 5 | 6 | Disclaimer: this project is currently very much work-in-progress. The motivating use case is simplifying snapshot testing using a combination of Vue.js, TypeScript, Jest, and html2jade. Additional functionality will be added as needed and breaking API changes may happen before releasing 1.0. Contributions are more than welcome. 7 | 8 | ## Usage 9 | Install: `npm install --save-dev vue-jest-utils` 10 | 11 | Note: Vue.js 2.0 and Jest must be installed as well. 12 | 13 | To use Jest with Vue.js single-file components (`*.vue`) or TypeScript sources, follow the guide on [vue-typescript-jest](https://github.com/locoslab/vue-typescript-jest). 14 | 15 | For a complete example of a TypeScript/Tsify/Vue.js/Vueify/Pug setup supporting Hot Module Replacement and unit/snapshot testing with Jest, cf. [vue-typescript-component-example](https://github.com/locoslab/vue-typescript-component-example). 16 | 17 | 18 | ### Examples 19 | 20 | #### JavaScript Test Example 21 | ```javascript 22 | const Vue = require('vue') 23 | const VJU = require('vue-jest-utils') 24 | 25 | const CounterJs = require('../src/counter-js.vue') 26 | 27 | describe('counter-js.vue', () => { 28 | it('should just work', () => { 29 | const vm = new Vue({ 30 | el: document.createElement('div'), 31 | render: (h) => h(CounterJs), 32 | }) 33 | VJU.clickNthButton(vm.$el, 1) 34 | VJU.clickNthButton(vm.$el, 3) 35 | VJU.clickNthButton(vm.$el, 2) 36 | // return a Promise that 37 | // 1. calls vm.nextTick() 38 | // 2. checks the snapshot of vm.$el using html2jade 39 | return VJU.expectToMatchSnapshot(vm) 40 | }) 41 | }) 42 | ``` 43 | 44 | #### TypeScript Test Example 45 | ```typescript 46 | /// 47 | 48 | import Vue = require('vue') 49 | import {expectToMatchSnapshot, clickNthButton} from 'vue-jest-utils' 50 | import CounterTs = require('../src/counter-ts.vue') 51 | 52 | describe('counter-ts.vue', () => { 53 | it('should just work', () => { 54 | const vm = new Vue({ 55 | el: document.createElement('div'), 56 | render: (h) => h(CounterTs), 57 | }) 58 | clickNthButton(vm.$el, 1) 59 | clickNthButton(vm.$el, 3) 60 | clickNthButton(vm.$el, 2) 61 | // return a Promise that 62 | // 1. calls vm.nextTick() 63 | // 2. checks the snapshot of vm.$el using html2jade 64 | return expectToMatchSnapshot(vm) 65 | }) 66 | }) 67 | ``` 68 | 69 | ## Contributing 70 | Contributions including bug reports, tests, and documentation are more than welcome. To get started with development: 71 | ``` bash 72 | # once: install dependencies 73 | npm install 74 | 75 | # run unit tests in watch mode 76 | npm test -- --watch 77 | 78 | # lint & test 79 | npm run prepublish 80 | ``` 81 | 82 | ## License 83 | [MIT](http://opensource.org/licenses/MIT) 84 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-jest-utils", 3 | "version": "0.2.0", 4 | "description": "Utilities for using Jest with Vue.js", 5 | "author": "Locoslab GmbH", 6 | "homepage": "https://github.com/locoslab/vue-jest-utils", 7 | "bugs": "https://github.com/locoslab/vue-jest-utils/issues", 8 | "repository": "locoslab/vue-jest-utils", 9 | "license": "MIT", 10 | "main": "./lib/vue-jest-utils.js", 11 | "typings": "./lib/vue-jest-utils.d.ts", 12 | "files": [ 13 | "lib" 14 | ], 15 | "keywords": [ 16 | "vue", 17 | "jest", 18 | "typescript", 19 | "testing" 20 | ], 21 | "scripts": { 22 | "test": "jest", 23 | "lint": "tslint src/**/*.ts test/**/*.ts", 24 | "prepublish": "tslint src/**/*.ts && jest --coverage --no-cache && tsc -p tsconfigDist.json" 25 | }, 26 | "jest": { 27 | "transform": { 28 | ".*\\.(ts|vue)$": "/node_modules/vue-typescript-jest/preprocessor.js" 29 | }, 30 | "moduleFileExtensions": [ 31 | "ts", 32 | "js", 33 | "vue" 34 | ], 35 | "testRegex": "/test/test/.*\\.(ts|js)$", 36 | "coveragePathIgnorePatterns": [ 37 | "/node_modules/", 38 | "/test/.*\\.(ts|js)$", 39 | "/.*\\.vue$" 40 | ] 41 | }, 42 | "babel": { 43 | "presets": [ 44 | "es2015" 45 | ] 46 | }, 47 | "peerDependencies": { 48 | "jest-cli": ">=16.0.0", 49 | "vue": "^2.0.1" 50 | }, 51 | "dependencies": { 52 | "html2jade": "^0.8.5" 53 | }, 54 | "devDependencies": { 55 | "@types/jest": "^19.0.0", 56 | "babel-core": "^6.0.0", 57 | "babel-plugin-transform-runtime": "^6.0.0", 58 | "babel-preset-es2015": "^6.0.0", 59 | "babel-runtime": "^6.0.0", 60 | "jest-cli": "^19.0.0", 61 | "pug": "^2.0.0-beta6", 62 | "tslint": "^5.1.0", 63 | "tslint-config-locoslab": "^1.0.0", 64 | "typescript": "^2.1.1", 65 | "vue": "^2.0.7", 66 | "vue-template-compiler": "^2.0.7", 67 | "vue-template-es2015-compiler": "^1.2.2", 68 | "vue-typescript-jest": "^0.3.0", 69 | "vue-typescript-import-dts": "^2.0.0" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/vue-jest-utils.ts: -------------------------------------------------------------------------------- 1 | import Html2jade = require('html2jade') 2 | import Vue = require('vue') 3 | 4 | /** convert html to pug as a Promise */ 5 | export function html2pug(html: string) { 6 | return new Promise((resolve, reject) => { 7 | Html2jade.convertHtml(html, {bodyless: true}, (err: any, jade: string) => { 8 | if (err) { 9 | reject(err) 10 | } else { 11 | resolve(jade) 12 | } 13 | }) 14 | }) 15 | } 16 | 17 | /** call vm.nextTick() as a Promise */ 18 | export function nextTick(vm: Vue) { 19 | return new Promise((resolve, reject) => { 20 | Vue.nextTick(() => resolve()) 21 | }) 22 | } 23 | 24 | /** call vm.nextTick() and check the snapshot as a Promise */ 25 | export function expectToMatchSnapshot(vm: Vue, element?: HTMLElement) { 26 | return new Promise((resolve, reject) => { 27 | nextTick(vm).then(() => html2pug((element || vm.$el).innerHTML)).then((jade) => { 28 | expect(jade).toMatchSnapshot() 29 | resolve() 30 | }).catch((err)=>(reject(err))) 31 | }) 32 | } 33 | 34 | /** click the nth button */ 35 | export function clickNthButton(el: HTMLElement, n: number) { 36 | (el.querySelector('button:nth-of-type(' + n + ')') as HTMLButtonElement).click() 37 | } 38 | -------------------------------------------------------------------------------- /test/src/counter-js.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 42 | -------------------------------------------------------------------------------- /test/src/counter-ts.ts: -------------------------------------------------------------------------------- 1 | const thisIsTypescript: string = 'counter-ts' 2 | export default { 3 | data () { 4 | return { 5 | name: thisIsTypescript, 6 | counter: 0 7 | } 8 | }, 9 | methods: { 10 | inc: function() { 11 | this.counter++ 12 | }, 13 | dec: function() { 14 | this.counter-- 15 | }, 16 | uncoveredFunction: function() { 17 | this.counter *= 2 18 | } 19 | }, 20 | computed: { 21 | inverse: { 22 | get: function() { 23 | return -this.counter 24 | }, 25 | set: function(value: number) { 26 | this.counter = -value 27 | } 28 | }, 29 | }, 30 | created: function () { 31 | this.counter++ 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/src/counter-ts.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /test/test/__snapshots__/counter-js.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`counter-js.vue should just work 1`] = ` 4 | "span(style='display: inline-block; width: 6em;') counter-js 5 | span(style='display: inline-block; width: 3em; text-align: center;') -3 6 | button(href='#', style='display: inline-block; width: 2em;') + 7 | button(href='#', style='display: inline-block; width: 2em;') - 8 | button(href='#', style='display: inline-block; width: 2em;') ! 9 | span(style='display: inline-block; width: 3em; text-align: center;') 3 10 | " 11 | `; 12 | -------------------------------------------------------------------------------- /test/test/__snapshots__/counter-ts.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`counter-ts.vue should just work 1`] = ` 4 | "span(style='display: inline-block; width: 6em;') counter-ts 5 | span(style='display: inline-block; width: 3em; text-align: center;') -3 6 | button(href='#', style='display: inline-block; width: 2em;') + 7 | button(href='#', style='display: inline-block; width: 2em;') - 8 | button(href='#', style='display: inline-block; width: 2em;') ! 9 | span(style='display: inline-block; width: 3em; text-align: center;') 3 10 | " 11 | `; 12 | -------------------------------------------------------------------------------- /test/test/counter-js.spec.js: -------------------------------------------------------------------------------- 1 | const Vue = require('vue') 2 | const VJU = require('../../src/vue-jest-utils.ts') 3 | 4 | const CounterJs = require('../src/counter-js.vue') 5 | 6 | describe('counter-js.vue', () => { 7 | it('should just work', () => { 8 | const vm = new Vue({ 9 | el: document.createElement('div'), 10 | render: (h) => h(CounterJs), 11 | }) 12 | VJU.clickNthButton(vm.$el, 1) 13 | VJU.clickNthButton(vm.$el, 3) 14 | VJU.clickNthButton(vm.$el, 2) 15 | return VJU.expectToMatchSnapshot(vm) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /test/test/counter-ts.spec.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import Vue = require('vue') 4 | import {expectToMatchSnapshot, clickNthButton} from '../../src/vue-jest-utils' 5 | import CounterTs = require('../src/counter-ts.vue') 6 | 7 | describe('counter-ts.vue', () => { 8 | it('should just work', () => { 9 | const vm = new Vue({ 10 | el: document.createElement('div'), 11 | render: (h) => h(CounterTs), 12 | }) 13 | clickNthButton(vm.$el, 1) 14 | clickNthButton(vm.$el, 3) 15 | clickNthButton(vm.$el, 2) 16 | return expectToMatchSnapshot(vm) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "noImplicitAny": true, 6 | "strictNullChecks": true, 7 | "noUnusedLocals": true, 8 | "sourceMap": false, 9 | "pretty": true, 10 | "declaration": true, 11 | "lib": ["es5", "dom", "es2015.core", "es2015.promise"] 12 | }, 13 | "exclude": [ 14 | "node_modules", 15 | "lib" 16 | ], 17 | "atom": { 18 | "rewriteTsconfig": false 19 | }, 20 | "compileOnSave": false 21 | } 22 | -------------------------------------------------------------------------------- /tsconfigDist.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "compilerOptions": { 4 | "outDir": "lib" 5 | }, 6 | "exclude": [ 7 | "node_modules", 8 | "lib", 9 | "test" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint-config-locoslab" 3 | } 4 | -------------------------------------------------------------------------------- /types/html2jade.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'html2jade' { 2 | function convertHtml(html: string, options?: Object, callback?: (err: any, jade: string) => void): void 3 | function convertDocument(html: string, options?: Object, callback?: (err: any, jade: string) => void): void 4 | } 5 | --------------------------------------------------------------------------------