├── .travis.yml ├── test ├── types │ └── html2jade.d.ts ├── test │ ├── __snapshots__ │ │ ├── counter-js.spec.js.snap │ │ ├── counter-ts.spec.ts.snap │ │ └── counter-html-ts.spec.js.snap │ ├── counter-js.spec.js │ ├── counter-html-ts.spec.js │ └── counter-ts.spec.ts └── src │ ├── counter-ts.vue │ ├── counter-ts.ts │ ├── counter-html-ts.vue │ └── counter-js.vue ├── tsconfig.json ├── LICENSE ├── package.json ├── preprocessor.js ├── .gitignore └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6" 4 | script: npm run prepublish 5 | -------------------------------------------------------------------------------- /test/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 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "noImplicitAny": true, 6 | "strictNullChecks": true, 7 | "sourceMap": false, 8 | "pretty": true, 9 | "lib": ["es5", "dom", "es2015.promise"], 10 | "types": ["vue-typescript-import-dts"] 11 | }, 12 | "exclude": [ 13 | "node_modules" 14 | ], 15 | "atom": { 16 | "rewriteTsconfig": false 17 | }, 18 | "compileOnSave": false 19 | } 20 | -------------------------------------------------------------------------------- /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/__snapshots__/counter-html-ts.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`counter-html-ts.vue should just work 1`] = ` 4 | "span(style='display: inline-block; width: 6em;') counter-ts 5 | | 6 | span(style='display: inline-block; width: 3em; text-align: center;') -3 7 | | 8 | button(href='#', style='display: inline-block; width: 2em;') + 9 | | 10 | button(href='#', style='display: inline-block; width: 2em;') - 11 | | 12 | button(href='#', style='display: inline-block; width: 2em;') ! 13 | | 14 | span(style='display: inline-block; width: 3em; text-align: center;') 3 15 | " 16 | `; 17 | -------------------------------------------------------------------------------- /test/src/counter-ts.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /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 | name: 'counter-ts' 34 | } 35 | -------------------------------------------------------------------------------- /test/src/counter-html-ts.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /test/src/counter-js.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 42 | -------------------------------------------------------------------------------- /test/test/counter-js.spec.js: -------------------------------------------------------------------------------- 1 | const Vue = require('vue') 2 | const html2jade = require('html2jade') 3 | 4 | const CounterJs = require('../src/counter-js.vue') 5 | 6 | describe('counter-js.vue', () => { 7 | it('should initialize correctly', () => { 8 | const vm = new Vue({ 9 | el: document.createElement('div'), 10 | render: (h) => h(CounterJs), 11 | }) 12 | expect(vm.$el.querySelector('div span').textContent).toBe('counter-js') 13 | expect(vm.$el.querySelector('div span:nth-child(2)').textContent).toBe('1') 14 | }) 15 | }) 16 | 17 | describe('counter-js.vue', () => { 18 | it('should just work', () => new Promise(function(resolve, reject) { 19 | const vm = new Vue({ 20 | el: document.createElement('div'), 21 | render: (h) => h(CounterJs), 22 | }) 23 | vm.$el.querySelector('div button:nth-of-type(1)').click() 24 | vm.$el.querySelector('div button:nth-of-type(3)').click() 25 | vm.$el.querySelector('div button:nth-of-type(2)').click() 26 | Vue.nextTick( () => { 27 | html2jade.convertHtml(vm.$el.innerHTML, {bodyless: true}, (err, jade) => { 28 | expect(jade).toMatchSnapshot() 29 | resolve() 30 | }) 31 | }) 32 | })) 33 | }) 34 | -------------------------------------------------------------------------------- /test/test/counter-html-ts.spec.js: -------------------------------------------------------------------------------- 1 | const Vue = require('vue') 2 | const html2jade = require('html2jade') 3 | 4 | const CounterHtmlTs = require('../src/counter-html-ts.vue') 5 | 6 | describe('counter-html-ts.vue', () => { 7 | it('should initialize correctly', () => { 8 | const vm = new Vue({ 9 | el: document.createElement('div'), 10 | render: (h) => h(CounterHtmlTs), 11 | }) 12 | expect(vm.$el.querySelector('div span').textContent).toBe('counter-ts') 13 | expect(vm.$el.querySelector('div span:nth-child(2)').textContent).toBe('1') 14 | }) 15 | }) 16 | 17 | describe('counter-html-ts.vue', () => { 18 | it('should just work', () => new Promise(function(resolve, reject) { 19 | const vm = new Vue({ 20 | el: document.createElement('div'), 21 | render: (h) => h(CounterHtmlTs), 22 | }) 23 | vm.$el.querySelector('div button:nth-of-type(1)').click() 24 | vm.$el.querySelector('div button:nth-of-type(3)').click() 25 | vm.$el.querySelector('div button:nth-of-type(2)').click() 26 | Vue.nextTick( () => { 27 | html2jade.convertHtml(vm.$el.innerHTML, {bodyless: true}, (err, jade) => { 28 | expect(jade).toMatchSnapshot() 29 | resolve() 30 | }) 31 | }) 32 | })) 33 | }) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Locoslab GmbH 4 | Copyright (c) 2014-2016 Evan You for portions used from https://github.com/vuejs/vueify 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /test/test/counter-ts.spec.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import Vue = require('vue') 4 | // see note about importing *.vue files 5 | import CounterTs = require('../src/counter-ts.vue') 6 | 7 | // basic unit testing 8 | describe('counter-ts.vue', () => { 9 | it('should initialize correctly', () => { 10 | const vm = new Vue({ 11 | el: document.createElement('div'), 12 | render: (h) => h(CounterTs), 13 | }) 14 | expect(vm.$el.querySelector('div span').textContent).toBe('counter-ts') 15 | expect(vm.$el.querySelector('div span:nth-child(2)').textContent).toBe('1') 16 | }) 17 | }) 18 | 19 | // or use snapshot testing, e.g., with html2jade 20 | function clickNthButton(el: HTMLElement, n: number) { 21 | (el.querySelector('div button:nth-of-type(' + n + ')')).click() 22 | } 23 | import html2jade = require('html2jade') 24 | 25 | describe('counter-ts.vue', () => { 26 | it('should just work', () => new Promise(function(resolve, reject) { 27 | const vm = new Vue({ 28 | el: document.createElement('div'), 29 | render: (h) => h(CounterTs), 30 | }) 31 | clickNthButton(vm.$el, 1) 32 | clickNthButton(vm.$el, 3) 33 | clickNthButton(vm.$el, 2) 34 | Vue.nextTick( () => { 35 | html2jade.convertHtml(vm.$el.innerHTML, {bodyless: true}, (err: any, jade: string) => { 36 | expect(jade).toMatchSnapshot() 37 | resolve() 38 | }) 39 | }) 40 | })) 41 | }) 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-typescript-jest", 3 | "version": "0.3.1", 4 | "description": "Jest preprocessor.js for Vue.js components and TypeScript", 5 | "author": "Locoslab GmbH", 6 | "homepage": "https://github.com/locoslab/vue-typescript-jest", 7 | "bugs": "https://github.com/locoslab/vue-typescript-jest/issues", 8 | "repository": "locoslab/vue-typescript-jest", 9 | "license": "MIT", 10 | "main": "./preprocessor.js", 11 | "files": [ 12 | "preprocessor.js" 13 | ], 14 | "keywords": [ 15 | "jest", 16 | "vue", 17 | "vueify", 18 | "typescript", 19 | "pug" 20 | ], 21 | "scripts": { 22 | "test": "jest", 23 | "prepublish": "eslint preprocessor.js && jest --no-cache --coverage" 24 | }, 25 | "jest": { 26 | "transform": { 27 | ".*\\.(ts|vue)$": "/preprocessor.js" 28 | }, 29 | "moduleFileExtensions": [ 30 | "ts", 31 | "js", 32 | "vue" 33 | ], 34 | "testRegex": "/test/test/.*\\.(ts|js)$", 35 | "coveragePathIgnorePatterns": [ 36 | "/node_modules/", 37 | "/test/test/.*\\.(ts|js)$", 38 | "/.*\\.vue$" 39 | ] 40 | }, 41 | "devDependencies": { 42 | "@types/jest": "^19.0.0", 43 | "babel-core": "^6.0.0", 44 | "babel-plugin-transform-runtime": "^6.0.0", 45 | "babel-preset-es2015": "^6.0.0", 46 | "babel-runtime": "^6.0.0", 47 | "eslint": "^3.7.0", 48 | "eslint-config-locoslab": "0.1.0", 49 | "html2jade": "^0.8.4", 50 | "jest-cli": "^19.0.0", 51 | "pug": "^2.0.0-beta6", 52 | "typescript": "^2.0.3", 53 | "vue": "^2.0.7", 54 | "vue-template-compiler": "^2.0.7", 55 | "vue-template-es2015-compiler": "^1.2.2", 56 | "vue-typescript-import-dts": "^2.0.0" 57 | }, 58 | "babel": { 59 | "presets": [ 60 | "es2015" 61 | ] 62 | }, 63 | "eslintConfig": { 64 | "extends": "locoslab", 65 | "env": { 66 | "node": true 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /preprocessor.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | 4 | var defaultBabelOptions = { 5 | presets: ['es2015'], 6 | plugins: ['transform-runtime'], 7 | } 8 | 9 | function ts(src, filePath) { 10 | // Microsoft's ts.findConfigFilepath does not work under Windows (hard coded '/' as directory separator) 11 | // https://github.com/Microsoft/TypeScript/pull/9625 probably fixes this but is open since July 11th 12 | function findConfigFile(filePath) { 13 | let prev = null 14 | do { 15 | const testPath = path.join(filePath, 'tsconfig.json') 16 | if (fs.existsSync(testPath)) { 17 | return testPath 18 | } 19 | prev = filePath 20 | filePath = path.dirname(filePath) 21 | } while (prev !== filePath) 22 | return undefined 23 | } 24 | const ts = require('typescript') 25 | const tsConfigPath = findConfigFile(filePath) 26 | if (!tsConfigPath) { 27 | throw 'tsconfig.json not found for ' + filePath 28 | } 29 | const tsOptions = require(tsConfigPath) 30 | const options = { 31 | compilerOptions: tsOptions.compilerOptions, 32 | moduleName: '', 33 | reportDiagnostics: true, 34 | fileName: filePath, 35 | } 36 | const res = ts.transpileModule(src, options) 37 | if (res.diagnostics.length > 0) { 38 | let err = '' 39 | res.diagnostics.forEach(diagnostic => { 40 | const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start) 41 | const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n') 42 | err += `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}\n` 43 | }) 44 | throw err 45 | } 46 | return res.outputText 47 | } 48 | 49 | function babel(src) { 50 | let babelc 51 | try { 52 | babelc = require('babel-core') 53 | } catch (e) { 54 | // do nothing 55 | } 56 | if (babelc) { 57 | return babelc.transform(src, defaultBabelOptions).code 58 | } else { 59 | return src 60 | } 61 | } 62 | 63 | // this is heavily based on vueify (Copyright (c) 2014-2016 Evan You) 64 | function vue(src, filePath) { 65 | function toFunction (code) { 66 | const transpile = require('vue-template-es2015-compiler') 67 | return transpile('function render () {' + code + '}') 68 | } 69 | const vueCompiler = require('vue-template-compiler') 70 | const parts = vueCompiler.parseComponent(src, { pad: true }) 71 | let script = '' 72 | if (!parts.script.lang) { 73 | script = babel(parts.script.content) 74 | } else { 75 | throw filePath + ': unknown 103 | ``` 104 | * Code coverage of `*.vue` files fails as the generated code contains a `with` statement that trips the babylon parser: use `coveragePathIgnorePatterns` as shown above to ignore the `*.vue` files 105 | 106 | ## Contributing 107 | Contributions including bug reports, tests, and documentation are more than welcome. To get started with development: 108 | ``` bash 109 | # once: install dependencies 110 | npm install 111 | 112 | # run unit tests in watch mode 113 | npm test -- --watch 114 | 115 | # lint & test 116 | npm run prepublish 117 | ``` 118 | 119 | ## License 120 | [MIT](http://opensource.org/licenses/MIT) 121 | --------------------------------------------------------------------------------