Merge pull request #48 from xuexb/feature/karma.tests

添加单元测试
This commit is contained in:
贤心 2017-08-31 14:48:02 +08:00 committed by GitHub
commit 94325aea10
30 changed files with 504 additions and 11 deletions

1
.gitignore vendored
View File

@ -26,3 +26,4 @@ dir.txt
release/ release/
build/ build/
coverage/

17
.travis.yml Normal file
View File

@ -0,0 +1,17 @@
language: node_js
dist: trusty
addons:
chrome: stable
node_js:
- node
#addons:
# sauce_connect: true
sudo: false
cache:
directories:
- node_modules
script:
- npm run test:cov
#- npm run test:sauce
after_script:
- npm install coveralls && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage

125
karma.conf.base.js Normal file
View File

@ -0,0 +1,125 @@
/**
* @file karma自动化测试配置
* @author fe.xiaowu@gmail.com
*/
/**
* 源文件
*
* @type {Array}
*/
var sourceFileMap = [
'src/layui.js',
'src/lay/modules/jquery.js',
'src/lay/modules/carousel.js',
'src/lay/modules/code.js',
'src/lay/modules/element.js',
'src/lay/modules/flow.js',
'src/lay/modules/form.js',
'src/lay/modules/laydate.js',
'src/lay/modules/layedit.js',
'src/lay/modules/layer.js',
'src/lay/modules/laypage.js',
'src/lay/modules/laytpl.js',
'src/lay/modules/table.js',
'src/lay/modules/tree.js',
'src/lay/modules/upload.js',
'src/lay/modules/util.js',
'src/lay/modules/mobile/zepto.js',
'src/lay/modules/mobile/layer-mobile.js',
'src/lay/modules/mobile/upload-mobile.js'
];
/**
* 测试覆盖率文件, 要忽略 jquery.jszepto.js
*
* @type {Object}
*/
var coverageFileMap = {};
sourceFileMap.filter(function (uri) {
return !/(jquery|zepto)\.js$/.test(uri);
}).forEach(function (uri) {
coverageFileMap[uri] = ['coverage'];
});
module.exports = function (config) {
return {
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// Important: 所有插件必须在此声明
plugins: ['karma-*'],
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
// Important: 下列数组中文件将『逆序载入』
frameworks: ['mocha', 'chai', 'chai-sinon'],
// list of files / patterns to load in the browser
files: sourceFileMap.concat('test/**/*.js'),
// list of files to exclude
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: coverageFileMap,
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: [
'mocha'
// 'coverage'
],
coverageReporter: {
// specify a common output directory
dir: '.',
reporters: [
// { type: 'html', subdir: 'report-html' },
{
type: 'lcov',
subdir: 'coverage'
},
{
type: 'text-summary'
}
]
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
// Note: 如果要调试Karma请设置为DEBUG
logLevel: config.LOG_INFO,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: [
'PhantomJS'
],
// enable / disable watching file and executing tests whenever any file changes
// Note: 代码改动自动运行测试需要singleRun为false
autoWatch: false,
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
// 脚本调用请设为 true
singleRun: true
};
};

105
karma.conf.sauce.js Normal file
View File

@ -0,0 +1,105 @@
/**
* @file karma配置
* @author fe.xiaowu@gmail.com
*/
var base = require('./karma.conf.base.js');
var customLaunchers = {
// Safari
sl_ios_safari: {
base: 'SauceLabs',
browserName: 'Safari'
},
// 安卓浏览器
sl_android_4_4: {
base: 'SauceLabs',
browserName: 'android',
version: '4.4'
},
sl_android_5: {
base: 'SauceLabs',
browserName: 'android',
version: '5'
},
sl_android_6: {
base: 'SauceLabs',
browserName: 'android',
version: '6'
},
// chrome
sl_ios_chrome: {
base: 'SauceLabs',
browserName: 'chrome'
},
sl_ie_8: {
base: 'SauceLabs',
browserName: 'internet explorer',
version: '8'
},
sl_ie_9: {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 7',
version: '9'
},
sl_ie_10: {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 8',
version: '10'
},
sl_ie_11: {
base: 'SauceLabs',
browserName: 'internet explorer',
platform: 'Windows 8.1',
version: '11'
},
sl_firefox: {
base: 'SauceLabs',
browserName: 'firefox',
platform: 'Windows 7'
}
};
// 不支持本地运行
if (!process.env.TRAVIS) {
console.error('不支持本地运行, 请使用 npm run test!');
process.exit(1);
}
// 变量检查
if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) {
console.error('---------------');
console.error('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are set.');
console.error('---------------');
process.exit(1);
}
module.exports = function (config) {
var options = Object.assign(base(config), {
reporters: ['mocha', 'saucelabs'],
sauceLabs: {
'testName': 'layui test case',
'recordVideo': false,
'recordScreenshots': false,
'startConnect': false,
'connectOptions': {
'no-ssl-bump-domains': 'all'
},
'public': 'public',
'build': process.env.CIRCLE_BUILD_NUM || process.env.SAUCE_BUILD_ID || 'build-' + Date.now(),
'tunnelIdentifier': process.env.TRAVIS_JOB_NUMBER
},
customLaunchers: customLaunchers,
browsers: Object.keys(customLaunchers),
captureTimeout: 1000 * 60 * 5,
browserNoActivityTimeout: 1000 * 60 * 5
});
config.set(options);
};

12
karma.conf.unit.js Normal file
View File

@ -0,0 +1,12 @@
/**
* @file karma配置
* @author fe.xiaowu@gmail.com
*/
var base = require('./karma.conf.base.js');
module.exports = function (config) {
var options = Object.assign(base(config), {});
config.set(options);
};

View File

@ -5,7 +5,10 @@
"main": "layui.js", "main": "layui.js",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "karma start karma.conf.unit.js",
"test:cov": "npm test -- --reporters mocha,coverage",
"test:sauce": "karma start karma.conf.sauce.js",
"test:watch": "npm test -- --auto-watch --no-single-run"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -23,7 +26,19 @@
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-uglify": "^1.5.4", "gulp-uglify": "^1.5.4",
"gulp-zip": "^4.0.0", "gulp-zip": "^4.0.0",
"minimist": "^1.2.0" "minimist": "^1.2.0",
"chai": "^3.5.0",
"karma": "^1.5.0",
"karma-chai": "^0.1.0",
"karma-chai-sinon": "^0.1.5",
"karma-coverage": "^1.1.1",
"karma-mocha": "^1.3.0",
"karma-sauce-launcher": "^1.1.0",
"karma-mocha-reporter": "^2.2.3",
"karma-phantomjs-launcher": "^1.0.4",
"mocha": "^3.2.0",
"sinon": "^2.0.0",
"sinon-chai": "^2.8.0"
}, },
"bugs": { "bugs": {
"url": "https://github.com/sentsin/layui/issues" "url": "https://github.com/sentsin/layui/issues"
@ -33,15 +48,6 @@
"test": "test" "test": "test"
}, },
"dependencies": { "dependencies": {
"gulp": "^3.9.1",
"gulp-uglify": "^1.5.4",
"gulp-minify-css": "^1.2.4",
"gulp-concat": "^2.6.0",
"gulp-header": "^1.8.8",
"gulp-if": "^2.0.1",
"gulp-rename": "^1.2.2",
"del": "^2.2.2",
"minimist": "^1.2.0"
}, },
"keywords": [ "keywords": [
"layui", "layui",

227
test/laytpl.js Normal file
View File

@ -0,0 +1,227 @@
/**
* @file laytpl - 测试
* @author xuexb <fe.xiaowu@gmail.com>
*/
/* global layui */
var laytpl = layui.laytpl;
describe('laytpl', function () {
it('param is error', function () {
[
[], {},
null,
1,
true
].forEach(function (key) {
expect(laytpl(key)).to.have.string('Laytpl Error');
});
});
it('async render callback', function (done) {
expect(laytpl('').render()).to.have.string('Laytpl Error');
laytpl('{{ d.name }}是一位公猿').render({
name: '贤心'
}, function (result) {
expect(result).to.equal('贤心是一位公猿');
done();
});
});
it('sync result', function () {
var result = laytpl('{{ d.name }}是一位公猿').render({
name: '贤心'
});
expect(result).to.equal('贤心是一位公猿');
});
it('cached', function () {
var compile = laytpl('{{ d.name }}');
expect(compile.render({
name: 1
})).to.equal('1');
expect(compile.render({
name: 2
})).to.equal('2');
});
it('unescape result', function () {
var result = laytpl('{{ d.name }}<div></div>').render({
name: '<em>laytpl</em>'
});
expect(result).to.equal('<em>laytpl</em><div></div>');
});
it('escape result', function () {
var result = laytpl('{{= d.name }}<div></div>').render({
name: '<em>laytpl</em>'
});
expect(result).to.equal('&lt;em&gt;laytpl&lt;/em&gt;<div></div>');
});
describe('typeof result', function () {
it('string', function () {
expect(laytpl('{{ d.name }}').render({
name: 1
})).to.be.a('string');
expect(laytpl([
'{{# ',
' if (true) {',
' return "1";',
' }',
'}}'
].join('')).render({})).to.be.a('string');
});
// 表达式返回boolean
it('boolean', function () {
expect(laytpl([
'{{# ',
' if (true) {',
' return true;',
' }',
'}}'
].join('')).render({})).to.be.a('boolean');
});
it('number', function () {
expect(laytpl([
'{{# ',
' if (true) {',
' return 1;',
' }',
'}}'
].join('')).render({})).to.be.a('number');
});
});
describe('method config', function () {
// reset
afterEach(function () {
laytpl.config({
open: '{{',
close: '}}'
});
});
it('typeof', function () {
expect(laytpl.config).to.be.a('function');
});
it('param is empty', function () {
expect(laytpl.config()).to.be.undefined;
});
it('set open', function () {
laytpl.config({
open: '<%'
});
var result = laytpl([
'<%# var name = "laytpl"; }}',
'你好, <% name }}, <% d.date }}'
].join('')).render({
date: '2017'
});
expect(result).to.equal('你好, laytpl, 2017');
});
it('set open and close', function () {
laytpl.config({
open: '<%',
close: '%>'
});
var result = laytpl([
'<%# var name = "laytpl"; %>',
'你好, <% name %>, <% d.date %>'
].join('')).render({
date: '2017'
});
expect(result).to.equal('你好, laytpl, 2017');
});
});
describe('js expression', function () {
it('var', function () {
var result = laytpl('{{# var type = 1; }}{{ type }}{{ d.name }}').render({
name: 2
});
expect(result).to.equal('12');
});
it('function', function () {
var result = laytpl('{{# var fn = function () {return "ok"}; }}{{ fn() }}').render({});
expect(result).to.equal('ok');
});
it('for', function () {
var result = laytpl([
'{{# ',
' var fn = function () {',
' var num = 0;',
' for (var i = 0; i < 10;i++) {',
' num += 1;',
' }',
' return num',
' };',
'}}',
'{{# ',
' var name = "laytpl";',
'}}',
'你好, {{ name }}, {{ d.name }}, {{ fn() }}'
].join('')).render({
name: 'ok'
});
expect(result).to.equal('你好, laytpl, ok, 10');
});
it('if else', function () {
var result;
result = laytpl([
'{{# ',
' if (true) {',
' return true;',
' }',
' else {',
' return false',
' }',
'}}'
].join('')).render({});
expect(result).to.be.true;
result = laytpl([
'{{# ',
' if (!true) {',
' return true;',
' }',
' else {',
' return false',
' }',
'}}'
].join('')).render({});
expect(result).to.be.false;
});
});
describe('parse error', function () {
it('error var', function () {
var result = laytpl('{{ data.xxoo }}').render({});
expect(result).to.have.string('Can\'t find variable: data');
expect(result).to.have.string('Laytpl Error');
});
it('error expression', function () {
var result = laytpl('{{# var xxoo = ; }}').render({});
expect(result).to.have.string('Laytpl Error');
expect(result).to.have.string('Unexpected token \';\'');
});
});
});