From 9b6ff1ce28f5b85b86a886986ee8d5de7bb87fd9 Mon Sep 17 00:00:00 2001 From: huixisheng Date: Sat, 4 Mar 2017 00:09:02 +0800 Subject: [PATCH] add test and update webpack config [new] add test config [new] add breadcrumb test [change] update package.json [new] util.js copied from element [unresolved] see test/unit/index.js @todo --- .gitignore | 4 +- build/webpack.base.config.js | 10 +++- build/webpack.dev.config.js | 4 +- build/webpack.test.config.js | 25 +++++++++ package.json | 23 +++++++- test | 0 test/unit/index.js | 18 +++++++ test/unit/karma.conf.js | 33 ++++++++++++ test/unit/specs/breadcrumb.spec.js | 24 +++++++++ test/unit/util.js | 85 ++++++++++++++++++++++++++++++ 10 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 build/webpack.test.config.js delete mode 100644 test create mode 100644 test/unit/index.js create mode 100644 test/unit/karma.conf.js create mode 100644 test/unit/specs/breadcrumb.spec.js create mode 100644 test/unit/util.js diff --git a/.gitignore b/.gitignore index 19514b87..c5cfd2b4 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,7 @@ npm-debug.log *.swp *.swo *.log -test/dist/ +examples/dist/ dist/ +yarn-error.log +test/unit/coverage \ No newline at end of file diff --git a/build/webpack.base.config.js b/build/webpack.base.config.js index 80fe22c7..2a258deb 100644 --- a/build/webpack.base.config.js +++ b/build/webpack.base.config.js @@ -2,6 +2,10 @@ * 公共配置 */ var webpack = require('webpack'); +var path = require('path'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} module.exports = { // 加载器 @@ -55,6 +59,10 @@ module.exports = { ] }, resolve: { - extensions: ['.js', '.vue'] + extensions: ['.js', '.vue'], + alias: { + 'vue': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } } }; diff --git a/build/webpack.dev.config.js b/build/webpack.dev.config.js index a587feee..699d29eb 100644 --- a/build/webpack.dev.config.js +++ b/build/webpack.dev.config.js @@ -8,6 +8,7 @@ var webpack = require('webpack'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var merge = require('webpack-merge') var webpackBaseConfig = require('./webpack.base.config.js'); +var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') module.exports = merge(webpackBaseConfig, { @@ -35,6 +36,7 @@ module.exports = merge(webpackBaseConfig, { inject: true, filename: path.join(__dirname, '../examples/dist/index.html'), template: path.join(__dirname, '../examples/index.html') - }) + }), + new FriendlyErrorsPlugin() ] }); diff --git a/build/webpack.test.config.js b/build/webpack.test.config.js new file mode 100644 index 00000000..6c3212b7 --- /dev/null +++ b/build/webpack.test.config.js @@ -0,0 +1,25 @@ +/** + * 用于单元测试 + */ + +var webpack = require('webpack') +var merge = require('webpack-merge') +var webpackBaseConfig = require('./webpack.base.config.js'); + + +var webpackConfig = merge(webpackBaseConfig, { + // use inline sourcemap for karma-sourcemap-loader + devtool: '#inline-source-map', + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"testing"' + } + }) + ] +}) + +// no need for app entry during tests +delete webpackConfig.entry + +module.exports = webpackConfig diff --git a/package.json b/package.json index 40a54235..7a89f0ea 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "dist:prod": "webpack --config build/webpack.dist.prod.config.js", "dist": "npm run dist:style && npm run dist:dev && npm run dist:prod", "lint": "eslint --fix --ext .js,.vue src", - "test": "npm run dist && npm run lint", + "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", + "test": "npm run unit", "prepublish": "npm run dist" }, "repository": { @@ -54,11 +55,14 @@ "babel-plugin-transform-runtime": "^6.12.0", "babel-preset-es2015": "^6.9.0", "babel-runtime": "^6.11.6", + "chai": "^3.5.0", + "cross-env": "^3.1.4", "css-loader": "^0.23.1", "eslint": "^3.12.2", "eslint-plugin-html": "^1.7.0", "extract-text-webpack-plugin": "^2.0.0", "file-loader": "^0.8.5", + "friendly-errors-webpack-plugin": "^1.6.1", "gulp": "^3.9.1", "gulp-autoprefixer": "^3.1.1", "gulp-clean-css": "^2.0.13", @@ -66,8 +70,21 @@ "gulp-rename": "^1.2.2", "html-loader": "^0.3.0", "html-webpack-plugin": "^2.28.0", + "karma": "^1.4.1", + "karma-coverage": "^1.1.1", + "karma-mocha": "^1.3.0", + "karma-phantomjs-launcher": "^1.0.2", + "karma-sinon-chai": "^1.2.4", + "karma-sourcemap-loader": "^0.3.7", + "karma-spec-reporter": "0.0.26", + "karma-webpack": "^2.0.2", "less": "^2.7.1", "less-loader": "^2.2.3", + "lolex": "^1.5.2", + "mocha": "^3.2.0", + "phantomjs-prebuilt": "^2.1.13", + "sinon": "^1.17.7", + "sinon-chai": "^2.8.0", "style-loader": "^0.13.1", "url-loader": "^0.5.7", "vue": "^2.2.1", @@ -80,5 +97,9 @@ "webpack": "^2.2.1", "webpack-dev-server": "^2.4.1", "webpack-merge": "^3.0.0" + }, + "engines": { + "node": ">= 4.0.0", + "npm": ">= 3.0.0" } } diff --git a/test b/test deleted file mode 100644 index e69de29b..00000000 diff --git a/test/unit/index.js b/test/unit/index.js new file mode 100644 index 00000000..b282cc31 --- /dev/null +++ b/test/unit/index.js @@ -0,0 +1,18 @@ +import Vue from 'vue'; +Vue.config.productionTip = false; + +// Polyfill fn.bind() for PhantomJS +/* eslint-disable no-extend-native */ +Function.prototype.bind = require('function-bind'); + +// require all test files (files that ends with .spec.js) +const testsContext = require.context('./specs', true, /\.spec$/); +testsContext.keys().forEach(testsContext); + +// require all src files except main.js for coverage. +// you can also change this to match only the subset of files that +// you want coverage for. +// const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/); +// @todo +const srcContext = require.context('../../src/components/breadcrumb', true, /^\.\/(?!styles.*?(\.less)?$)/); +srcContext.keys().forEach(srcContext); diff --git a/test/unit/karma.conf.js b/test/unit/karma.conf.js new file mode 100644 index 00000000..2fe05ea0 --- /dev/null +++ b/test/unit/karma.conf.js @@ -0,0 +1,33 @@ +// This is a karma config file. For more details see +// http://karma-runner.github.io/0.13/config/configuration-file.html +// we are also using it with karma-webpack +// https://github.com/webpack/karma-webpack + +var webpackConfig = require('../../build/webpack.test.config.js'); + +module.exports = function (config) { + config.set({ + // to run in additional browsers: + // 1. install corresponding karma launcher + // http://karma-runner.github.io/0.13/config/browsers.html + // 2. add it to the `browsers` array below. + browsers: ['PhantomJS'], + frameworks: ['mocha', 'sinon-chai'], + reporters: ['spec', 'coverage'], + files: ['./index.js'], + preprocessors: { + './index.js': ['webpack', 'sourcemap'] + }, + webpack: webpackConfig, + webpackMiddleware: { + noInfo: true, + }, + coverageReporter: { + dir: './coverage', + reporters: [ + { type: 'lcov', subdir: '.' }, + { type: 'text-summary' }, + ] + }, + }); +}; diff --git a/test/unit/specs/breadcrumb.spec.js b/test/unit/specs/breadcrumb.spec.js new file mode 100644 index 00000000..3166c415 --- /dev/null +++ b/test/unit/specs/breadcrumb.spec.js @@ -0,0 +1,24 @@ +import { createVue, destroyVM } from '../util'; + +describe('Breadcrumb.vue', () => { + let vm; + afterEach(() => { + destroyVM(vm); + }); + it('create', done => { + vm = createVue(` + + Home4 + Components + Breadcrumb + + `); + expect(vm.$el.querySelectorAll('.ivu-breadcrumb-item-link').length).to.equal(3); + + vm.$nextTick(_ => { + // console.log(vm.$el.querySelector('.ivu-breadcrumb-item-separator').innerHTML); + expect(vm.$el.querySelector('.ivu-breadcrumb-item-separator').innerHTML).to.equal('=>'); + done(); + }); + }); +}); \ No newline at end of file diff --git a/test/unit/util.js b/test/unit/util.js new file mode 100644 index 00000000..47cd3313 --- /dev/null +++ b/test/unit/util.js @@ -0,0 +1,85 @@ +import Vue from 'vue'; +import iView from '../../src/index'; + +Vue.use(iView); + +let id = 0; + +const createElm = function() { + const elm = document.createElement('div'); + + elm.id = 'app' + ++id; + document.body.appendChild(elm); + + return elm; +}; + +/** + * 回收 vm + * @param {Object} vm + */ +exports.destroyVM = function(vm) { + vm.$el && + vm.$el.parentNode && + vm.$el.parentNode.removeChild(vm.$el); +}; + +/** + * 创建一个 Vue 的实例对象 + * @param {Object|String} Compo 组件配置,可直接传 template + * @param {Boolean=false} mounted 是否添加到 DOM 上 + * @return {Object} vm + */ +exports.createVue = function(Compo, mounted = false) { + const elm = createElm(); + + if (Object.prototype.toString.call(Compo) === '[object String]') { + Compo = { template: Compo }; + } + return new Vue(Compo).$mount(mounted === false ? null : elm); +}; + +/** + * 创建一个测试组件实例 + * @link http://vuejs.org/guide/unit-testing.html#Writing-Testable-Components + * @param {Object} Compo - 组件对象 + * @param {Object} propsData - props 数据 + * @param {Boolean=false} mounted - 是否添加到 DOM 上 + * @return {Object} vm + */ +exports.createTest = function(Compo, propsData = {}, mounted = false) { + if (propsData === true || propsData === false) { + mounted = propsData; + propsData = {}; + } + const elm = createElm(); + const Ctor = Vue.extend(Compo); + return new Ctor({ propsData }).$mount(mounted === false ? null : elm); +}; + +/** + * 触发一个事件 + * mouseenter, mouseleave, mouseover, keyup, change, click 等 + * @param {Element} elm + * @param {String} name + * @param {*} opts + */ +exports.triggerEvent = function(elm, name, ...opts) { + let eventName; + + if (/^mouse|click/.test(name)) { + eventName = 'MouseEvents'; + } else if (/^key/.test(name)) { + eventName = 'KeyboardEvent'; + } else { + eventName = 'HTMLEvents'; + } + const evt = document.createEvent(eventName); + + evt.initEvent(name, ...opts); + elm.dispatchEvent + ? elm.dispatchEvent(evt) + : elm.fireEvent('on' + name, evt); + + return elm; +};