Authored by 郭成尧

merge_develop

  1 +root = true
  2 +
  3 +[*]
  4 +indent_style = space
  5 +indent_size = 4
  6 +end_of_line = lf
  7 +charset = utf-8
  8 +trim_trailing_whitespace = true
  9 +insert_final_newline = true
  10 +
  11 +[*.md]
  12 +trim_trailing_whitespace = false
  1 +bundle/**/*.js
  2 +dist/**/*.js
1 -  
2 -# Created by https://www.gitignore.io/api/node,webstorm,netbeans,sublimetext,vim  
3 -  
4 -### Node ###  
5 -# Logs  
6 -logs  
7 -*.log  
8 -npm-debug.log*  
9 -  
10 -# Runtime data  
11 -pids  
12 -*.pid  
13 -*.seed  
14 -  
15 -# Directory for instrumented libs generated by jscoverage/JSCover  
16 -lib-cov  
17 -  
18 -# Coverage directory used by tools like istanbul  
19 -coverage  
20 -  
21 -# nyc test coverage  
22 -.nyc_output  
23 -  
24 -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)  
25 -.grunt  
26 -  
27 -# node-waf configuration  
28 -.lock-wscript  
29 -  
30 -# Compiled binary addons (http://nodejs.org/api/addons.html)  
31 -build/Release  
32 -  
33 -# Dependency directories  
34 -node_modules  
35 -jspm_packages  
36 -  
37 -# Optional npm cache directory  
38 -.npm  
39 -  
40 -# Optional REPL history  
41 -.node_repl_history  
42 -  
43 -  
44 -### WebStorm ###  
45 -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm  
46 -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839  
47 -  
48 -# User-specific stuff:  
49 -.idea/workspace.xml  
50 -.idea/tasks.xml  
51 -.idea/dictionaries  
52 -.idea/vcs.xml  
53 -.idea/jsLibraryMappings.xml  
54 -  
55 -# Sensitive or high-churn files:  
56 -.idea/dataSources.ids  
57 -.idea/dataSources.xml  
58 -.idea/dataSources.local.xml  
59 -.idea/sqlDataSources.xml  
60 -.idea/dynamic.xml  
61 -.idea/uiDesigner.xml  
62 -  
63 -# Gradle:  
64 -.idea/gradle.xml  
65 -.idea/libraries  
66 -  
67 -# Mongo Explorer plugin:  
68 -.idea/mongoSettings.xml  
69 -  
70 -## File-based project format:  
71 -*.iws  
72 -  
73 -## Plugin-specific files:  
74 -  
75 -# IntelliJ  
76 -/out/  
77 -  
78 -# mpeltonen/sbt-idea plugin  
79 -.idea_modules/  
80 -  
81 -# JIRA plugin  
82 -atlassian-ide-plugin.xml  
83 -  
84 -# Crashlytics plugin (for Android Studio and IntelliJ)  
85 -com_crashlytics_export_strings.xml  
86 -crashlytics.properties  
87 -crashlytics-build.properties  
88 -fabric.properties  
89 -  
90 -### WebStorm Patch ###  
91 -# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721  
92 -  
93 -# *.iml  
94 -# modules.xml  
95 -  
96 -  
97 -### NetBeans ###  
98 -nbproject/private/  
99 -build/  
100 -nbbuild/  
101 -dist/  
102 -nbdist/  
103 -nbactions.xml  
104 -.nb-gradle/  
105 -  
106 -  
107 -### SublimeText ###  
108 -# cache files for sublime text  
109 -*.tmlanguage.cache  
110 -*.tmPreferences.cache  
111 -*.stTheme.cache  
112 -  
113 -# workspace files are user-specific  
114 -*.sublime-workspace  
115 -  
116 -# project files should be checked into the repository, unless a significant  
117 -# proportion of contributors will probably not be using SublimeText  
118 -# *.sublime-project  
119 -  
120 -# sftp configuration file  
121 -sftp-config.json  
122 -  
123 -  
124 -### Vim ###  
125 -# swap  
126 -[._]*.s[a-w][a-z]  
127 -[._]s[a-w][a-z]  
128 -# session  
129 -Session.vim  
130 -# temporary  
131 -.netrwhist  
132 -*~  
133 -# auto-generated tag files  
134 -tags  
135 -  
136 -### YOHO ###  
137 -dist 1 +
  2 +# Created by https://www.gitignore.io/api/node,webstorm,netbeans,sublimetext,vim
  3 +
  4 +### Node ###
  5 +# Logs
  6 +logs
  7 +*.log
  8 +npm-debug.log*
  9 +
  10 +# Runtime data
  11 +pids
  12 +*.pid
  13 +*.seed
  14 +
  15 +# Directory for instrumented libs generated by jscoverage/JSCover
  16 +lib-cov
  17 +
  18 +# Coverage directory used by tools like istanbul
  19 +coverage
  20 +
  21 +# nyc test coverage
  22 +.nyc_output
  23 +
  24 +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
  25 +.grunt
  26 +
  27 +# node-waf configuration
  28 +.lock-wscript
  29 +
  30 +# Compiled binary addons (http://nodejs.org/api/addons.html)
  31 +build/Release
  32 +
  33 +# Dependency directories
  34 +node_modules
  35 +jspm_packages
  36 +
  37 +# Optional npm cache directory
  38 +.npm
  39 +
  40 +# Optional REPL history
  41 +.node_repl_history
  42 +
  43 +
  44 +### WebStorm ###
  45 +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
  46 +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
  47 +.idea/
  48 +# User-specific stuff:
  49 +.idea/workspace.xml
  50 +.idea/tasks.xml
  51 +.idea/dictionaries
  52 +.idea/vcs.xml
  53 +.idea/jsLibraryMappings.xml
  54 +
  55 +# Sensitive or high-churn files:
  56 +.idea/dataSources.ids
  57 +.idea/dataSources.xml
  58 +.idea/dataSources.local.xml
  59 +.idea/sqlDataSources.xml
  60 +.idea/dynamic.xml
  61 +.idea/uiDesigner.xml
  62 +
  63 +# Gradle:
  64 +.idea/gradle.xml
  65 +.idea/libraries
  66 +
  67 +# Mongo Explorer plugin:
  68 +.idea/mongoSettings.xml
  69 +
  70 +## File-based project format:
  71 +*.iws
  72 +
  73 +## Plugin-specific files:
  74 +
  75 +# IntelliJ
  76 +/out/
  77 +
  78 +# mpeltonen/sbt-idea plugin
  79 +.idea_modules/
  80 +
  81 +# JIRA plugin
  82 +atlassian-ide-plugin.xml
  83 +
  84 +# Crashlytics plugin (for Android Studio and IntelliJ)
  85 +com_crashlytics_export_strings.xml
  86 +crashlytics.properties
  87 +crashlytics-build.properties
  88 +fabric.properties
  89 +
  90 +### WebStorm Patch ###
  91 +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
  92 +
  93 +# *.iml
  94 +# modules.xml
  95 +
  96 +
  97 +### NetBeans ###
  98 +nbproject/private/
  99 +build/
  100 +nbbuild/
  101 +dist/
  102 +nbdist/
  103 +nbactions.xml
  104 +.nb-gradle/
  105 +
  106 +
  107 +### SublimeText ###
  108 +# cache files for sublime text
  109 +*.tmlanguage.cache
  110 +*.tmPreferences.cache
  111 +*.stTheme.cache
  112 +
  113 +# workspace files are user-specific
  114 +*.sublime-workspace
  115 +
  116 +# project files should be checked into the repository, unless a significant
  117 +# proportion of contributors will probably not be using SublimeText
  118 +# *.sublime-project
  119 +
  120 +# sftp configuration file
  121 +sftp-config.json
  122 +
  123 +
  124 +### Vim ###
  125 +# swap
  126 +[._]*.s[a-w][a-z]
  127 +[._]s[a-w][a-z]
  128 +# session
  129 +Session.vim
  130 +# temporary
  131 +.netrwhist
  132 +*~
  133 +# auto-generated tag files
  134 +tags
  135 +
  136 +### YOHO ###
  137 +dist
138 public/css/* 138 public/css/*
139 -public/bundle/*  
140 -.eslintcache 139 +public/bundle/*
  140 +.eslintcache
  141 +*.log.*
  1 +css/**/*.css
  2 +dist/**/*.css
@@ -3,21 +3,26 @@ @@ -3,21 +3,26 @@
3 * @author: xuqi<qi.xu@yoho.cn> 3 * @author: xuqi<qi.xu@yoho.cn>
4 * @date: 2016/4/25 4 * @date: 2016/4/25
5 */ 5 */
  6 +'use strict';
6 7
7 -var express = require('express'), 8 +let express = require('express'),
8 path = require('path'), 9 path = require('path'),
9 bodyParser = require('body-parser'), 10 bodyParser = require('body-parser'),
10 - favicon = require('serve-favicon'),  
11 - cookieParser = require('cookie-parser'); 11 + cookieParser = require('cookie-parser'),
  12 + favicon = require('serve-favicon');
12 13
13 -var app = express(); 14 +require('express-handlebars');
  15 +
  16 +
  17 +let app = express();
14 18
15 app.set('view engine', '.hbs'); 19 app.set('view engine', '.hbs');
16 20
  21 +app.use(favicon(path.join(__dirname, '/public/favicon.ico')));
  22 +app.use(express.static(path.join(__dirname, 'public')));
17 app.use(bodyParser.json()); 23 app.use(bodyParser.json());
18 app.use(bodyParser.urlencoded({extended: false})); 24 app.use(bodyParser.urlencoded({extended: false}));
19 app.use(cookieParser()); 25 app.use(cookieParser());
20 -app.use(express.static(path.join(__dirname, 'public')));  
21 26
22 // dispatcher 27 // dispatcher
23 require('./dispatch')(app); 28 require('./dispatch')(app);
  1 +/**
  2 + * sub app guang
  3 + * @author: hbomb<qiqi.zhou@yoho.cn>
  4 + * @date: 2016/05/06
  5 + */
  6 +
  7 +var express = require('express'),
  8 + path = require('path'),
  9 + hbs = require('express-handlebars');
  10 +
  11 +var app = express();
  12 +
  13 +// set view engin
  14 +var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
  15 +
  16 +app.set('views', path.join(__dirname, 'views/action'));
  17 +app.engine('.hbs', hbs({
  18 + extname: '.hbs',
  19 + defaultLayout: 'layout',
  20 + layoutsDir: doraemon,
  21 + partialsDir: ['./views/partial', `${doraemon}/partial`],
  22 + helpers: 'helpers'
  23 +}));
  24 +
  25 +// router
  26 +require('./router')(app);
  27 +
  28 +module.exports = app;
  1 +/**
  2 + * router of sub app guang
  3 + * @author: hbomb<qiqi.zhou@yoho.cn>
  4 + * @date: 2016/05/06
  5 + */
  6 +
  7 +'use strict';
  8 +
  9 +
  10 +let API = require('../../library/api');
  11 +
  12 +// Your controller here
  13 +let router = function(app) {
  14 + app.get('/', function(req, res) {
  15 + let api = new API();
  16 + let name = '/productColor/queryProductColors';
  17 +
  18 + api.get(name, {}).then(function(body) {
  19 + res.send(body);
  20 + }).catch(function() {
  21 + res.send('something wrong');
  22 + });
  23 + });
  24 +};
  25 +
  26 +
  27 +
  28 +
  29 +module.exports = router;
  1 +/**
  2 + * 系统配置
  3 + *
  4 + * @author hbomb qiqi.zhou@yoho.cn
  5 + * @date 2016/05/06
  6 + */
  7 +
  8 +module.exports = {
  9 + domains: {
  10 + api: 'http://192.168.102.202:8088/platform'
  11 + },
  12 + loggers: {
  13 + infoFile: {
  14 + name: 'info',
  15 + level: 'info',
  16 + filename: 'info.log'
  17 + },
  18 + errorFile: {
  19 + name: 'error',
  20 + level: 'error',
  21 + filename: 'error.log',
  22 + handleExceptions: true
  23 + },
  24 + udp: { // send by udp
  25 + level: 'debug', // logger level
  26 + host: '192.168.102.162', // influxdb host
  27 + port: '4444'// influxdb port
  28 + },
  29 + console: {
  30 + level: 'debug',
  31 + colorize: 'all',
  32 + prettyPrint: true
  33 + }
  34 + }
  35 +};
@@ -8,21 +8,64 @@ @@ -8,21 +8,64 @@
8 8
9 const rp = require('request-promise'); 9 const rp = require('request-promise');
10 const _ = require('lodash'); 10 const _ = require('lodash');
  11 +const log = require('./logger');
  12 +const api = require('../config/common').domains.api;
  13 +const Timer = require('./timer');
11 14
12 -const ApiUrl = 'http://testapi.yoho.cn:28078/'; 15 +/**
  16 + * 日志打印
  17 + * @param {Object} requestPromise 请求的promise对象
  18 + */
  19 +function logPrint(options, requestPromise) {
  20 +
  21 + // 接口统计
  22 + let timer = new Timer();
  23 +
  24 + timer.put('getApi');// 统计时间开始
  25 +
  26 + let method = options.method ? options.method : 'get';
  27 +
  28 + log.info('API Begin %s call: %s, parms: %j',
  29 + method, options.url, options.qs, {});
  30 + requestPromise.then((body) => {
  31 + let duration = timer.put('getApi');// 接口返回
  32 +
  33 + log.info('API %s res: %s, parms: %j , durationMs: %d ms , body: %s',
  34 + method, options.url, options.qs, duration, body);
  35 + }).catch((error) => {
  36 + let duration = timer.put('getApi');// 接口返回
  37 +
  38 + log.error('API %s res: %s, parms: %j , durationMs: %d ms error: %s , statusCode: %d',
  39 + method, options.url, options.qs, duration, error.message, error.statusCode);
  40 + });
  41 +}
  42 +
  43 +
  44 +let ApiUrl;
13 45
14 class API { 46 class API {
15 47
  48 + constructor(url) {
  49 + ApiUrl = url || api;
  50 + }
  51 +
16 /** 52 /**
17 * get 53 * get
18 * @param url String 54 * @param url String
19 * @param data Obejct 55 * @param data Obejct
20 */ 56 */
21 get(url, data) { 57 get(url, data) {
22 - return rp({ 58 +
  59 + let options = {
23 url: `${ApiUrl}${url}`, 60 url: `${ApiUrl}${url}`,
24 qs: data 61 qs: data
25 - }); 62 + };
  63 +
  64 + let requestPromise = rp(options);
  65 +
  66 + logPrint(options, requestPromise);// 日志打印
  67 +
  68 + return requestPromise;
26 } 69 }
27 70
28 /** 71 /**
@@ -30,13 +73,19 @@ class API { @@ -30,13 +73,19 @@ class API {
30 * @params: urls => Array[Object[url[string], data[object]]] 73 * @params: urls => Array[Object[url[string], data[object]]]
31 */ 74 */
32 multiGet(urls) { 75 multiGet(urls) {
33 - var rps = []; 76 + let rps = [];
34 77
35 _.forEach(urls, function(el) { 78 _.forEach(urls, function(el) {
36 - rps.push(rp({ 79 + let options = {
37 url: `${ApiUrl}${el.url}`, 80 url: `${ApiUrl}${el.url}`,
38 qs: el.data 81 qs: el.data
39 - })); 82 + };
  83 +
  84 + let requestPromise = rp(options);
  85 +
  86 + logPrint(options, requestPromise);// 打印日志
  87 +
  88 + rps.push(requestPromise);
40 }); 89 });
41 90
42 return Promise.all(rps).then((d) => { 91 return Promise.all(rps).then((d) => {
@@ -50,11 +99,17 @@ class API { @@ -50,11 +99,17 @@ class API {
50 * @param data Obejct 99 * @param data Obejct
51 */ 100 */
52 post(url, data) { 101 post(url, data) {
53 - return rp({ 102 + let options = {
54 url: `${ApiUrl}${url}`, 103 url: `${ApiUrl}${url}`,
55 method: 'post', 104 method: 'post',
56 form: data 105 form: data
57 - }); 106 + };
  107 +
  108 + let requestPromise = rp(options);
  109 +
  110 + logPrint(options, requestPromise);// 打印日志
  111 +
  112 + return requestPromise;
58 } 113 }
59 } 114 }
60 115
  1 +
  2 +/**
  3 + * 日志工具类
  4 + * @author: hbomb<qiqi.zhou@yoho.cn>
  5 + * @date: 2016/05/06
  6 + */
  7 + 'use strict';
  8 +
  9 + let winston = require('winston'),
  10 + config = require('../config/common'),
  11 + FileTransport = require('winston-daily-rotate-file');
  12 +
  13 + require('influxdb-winston');
  14 +
  15 + let logger = new (winston.Logger)({
  16 + transports: [
  17 + new (FileTransport)(config.loggers.infoFile),
  18 + new (FileTransport)(config.loggers.errorFile),
  19 + new (winston.transports.UdpTransport)(config.loggers.udp),
  20 + new (winston.transports.Console)(config.loggers.console)
  21 + ]
  22 + });
  23 +
  24 + module.exports = logger;
@@ -31,7 +31,7 @@ const packageSort = argument => { @@ -31,7 +31,7 @@ const packageSort = argument => {
31 } 31 }
32 32
33 return newObj; 33 return newObj;
34 -} 34 +};
35 35
36 /** 36 /**
37 * 生成签名 37 * 生成签名
@@ -46,7 +46,7 @@ const makeSign = argument => { @@ -46,7 +46,7 @@ const makeSign = argument => {
46 }); 46 });
47 47
48 return md5(qs.join('&')).toLowerCase(); 48 return md5(qs.join('&')).toLowerCase();
49 -} 49 +};
50 50
51 // 生成API签名,调用后端接口的时候有私钥校验 51 // 生成API签名,调用后端接口的时候有私钥校验
52 exports.apiSign = (params) => { 52 exports.apiSign = (params) => {
@@ -91,4 +91,4 @@ exports.webSign = (params) => { @@ -91,4 +91,4 @@ exports.webSign = (params) => {
91 var webPrivateKey = 'yohobuyapp'; 91 var webPrivateKey = 'yohobuyapp';
92 92
93 return params.key === md5(md5(webPrivateKey) + params.uid); 93 return params.key === md5(md5(webPrivateKey) + params.uid);
94 -};  
  94 +};
  1 +'use strict';
  2 +
  3 +/**
  4 + * 计时类
  5 + * @example
  6 + * let timer = new Timer();
  7 + * timer.put('profile');
  8 + * timer.put('proflie'); // console output: 12.14
  9 + *
  10 + * @author: hbomb<qiqi.zhou@yoho.cn>
  11 + * @date: 2016/05/07
  12 + */
  13 +
  14 +class Timer {
  15 + constructor() {
  16 + this.timers = {};
  17 + }
  18 +
  19 + /**
  20 + * 打点计时
  21 + */
  22 + put(label) {
  23 + let labelTime = this.timers[label];
  24 +
  25 + if (labelTime) {
  26 + let duration = process.hrtime(labelTime);
  27 +
  28 + return this._round(duration[1]);
  29 + } else {
  30 + this.timers[label] = process.hrtime();
  31 + }
  32 + }
  33 +
  34 + /**
  35 + * 格式化成毫秒
  36 + * @param {Number} value 纳秒
  37 + */
  38 + _round(value) {
  39 + return Math.round(value / 10000) / 100;
  40 + }
  41 +
  42 +}
  43 +
  44 +module.exports = Timer;
  1 +const shelljs = require('shelljs');
  2 +const path = require('path');
  3 +
  4 +const changeFiles = {
  5 + js: shelljs.exec('git diff --cached --name-only --diff-filter=ACM | grep .js$', {silent: true}).stdout,
  6 + css: shelljs.exec('git diff --cached --name-only --diff-filter=ACM | grep .css$', {silent: true}).stdout
  7 +};
  8 +const lintPath = {
  9 + js: path.resolve('./node_modules/.bin/eslint'),
  10 + css: path.resolve('./node_modules/.bin/stylelint')
  11 +};
  12 +const lintResult = {
  13 + js: {},
  14 + css: {}
  15 +};
  16 +
  17 +const ext = process.platform === 'win32' ? '.cmd' : ''; // Windows 平台需要加后缀
  18 +
  19 +// 在执行检查脚本的时候,不显示 NPM 错误日志
  20 +if (!shelljs.grep('npm run -s', path.resolve('./.git/hooks/pre-commit')).stdout.trim()) {
  21 + shelljs.sed('-i', 'npm run', 'npm run -s', path.resolve('./.git/hooks/pre-commit'));
  22 +}
  23 +
  24 +if (changeFiles.js) {
  25 + changeFiles.js = changeFiles.js.replace(/\n/g, ' ');
  26 + lintResult.js = shelljs.exec(`${lintPath.js}${ext} -c .eslintrc --cache --fix ${changeFiles.js}`);
  27 +}
  28 +
  29 +if (changeFiles.css) {
  30 + changeFiles.css = changeFiles.css.replace(/\n/g, ' ');
  31 + lintResult.css = shelljs.exec(`${lintPath.css}${ext} --config .stylelintrc ${changeFiles.css}`);
  32 +}
  33 +
  34 +if (lintResult.js.code || lintResult.css.code) {
  35 + process.exit(lintResult.js.code || lintResult.css.code); // eslint-disable-line
  36 +}
@@ -12,9 +12,9 @@ @@ -12,9 +12,9 @@
12 "dev": "node_modules/.bin/nodemon -e js,hbs -i public/ app.js", 12 "dev": "node_modules/.bin/nodemon -e js,hbs -i public/ app.js",
13 "online": "NODE_ENV=\"production\" node app.js", 13 "online": "NODE_ENV=\"production\" node app.js",
14 "debug": "DEBUG=\"express:*\" node app.js", 14 "debug": "DEBUG=\"express:*\" node app.js",
15 - "lint-js": "node_modules/.bin/eslint -c .eslintrc --cache --fix `git diff --cached --name-only --diff-filter=ACM | grep .js$` app.js",  
16 - "lint-css": "node_modules/.bin/stylelint --config .stylelintrc `git diff --cached --name-only --diff-filter=ACM | grep .css$`",  
17 - "precommit": "npm run lint-js && npm run lint-css" 15 + "lint-js": "./node_modules/.bin/eslint -c .eslintrc --cache --fix .",
  16 + "lint-css": "./node_modules/.bin/stylelint --config .stylelintrc public/**/*.css",
  17 + "precommit": "node lint.js"
18 }, 18 },
19 "license": "MIT", 19 "license": "MIT",
20 "dependencies": { 20 "dependencies": {
@@ -22,11 +22,14 @@ @@ -22,11 +22,14 @@
22 "cookie-parser": "^1.4.1", 22 "cookie-parser": "^1.4.1",
23 "express": "^4.13.1", 23 "express": "^4.13.1",
24 "express-handlebars": "^3.0.0", 24 "express-handlebars": "^3.0.0",
25 - "lodash": "^4.8.2", 25 + "influxdb-winston": "^1.0.1",
  26 + "lodash": "^4.12.0",
26 "md5": "^2.1.0", 27 "md5": "^2.1.0",
27 "morgan": "^1.7.0", 28 "morgan": "^1.7.0",
28 - "request-promise": "^2.0.1",  
29 - "serve-favicon": "^2.3.0" 29 + "request-promise": "^3.0.0",
  30 + "serve-favicon": "^2.3.0",
  31 + "winston": "^2.2.0",
  32 + "winston-daily-rotate-file": "^1.0.1"
30 }, 33 },
31 "devDependencies": { 34 "devDependencies": {
32 "autoprefixer": "^6.3.6", 35 "autoprefixer": "^6.3.6",
@@ -39,6 +42,8 @@ @@ -39,6 +42,8 @@
39 "gulp-sourcemaps": "^2.0.0-alpha", 42 "gulp-sourcemaps": "^2.0.0-alpha",
40 "gulp-util": "^3.0.7", 43 "gulp-util": "^3.0.7",
41 "husky": "^0.11.4", 44 "husky": "^0.11.4",
  45 + "mocha": "^2.4.5",
  46 + "nodemon": "1.9.2",
42 "postcss-assets": "^4.0.1", 47 "postcss-assets": "^4.0.1",
43 "postcss-cachebuster": "^0.1.2", 48 "postcss-cachebuster": "^0.1.2",
44 "postcss-calc": "^5.2.1", 49 "postcss-calc": "^5.2.1",
@@ -51,11 +56,12 @@ @@ -51,11 +56,12 @@
51 "postcss-sprites": "^3.1.2", 56 "postcss-sprites": "^3.1.2",
52 "postcss-use": "^2.0.2", 57 "postcss-use": "^2.0.2",
53 "precss": "^1.4.0", 58 "precss": "^1.4.0",
54 - "stylelint": "^6.2.2",  
55 - "stylelint-config-yoho": "^1.2.0", 59 + "rewire": "^2.5.1",
  60 + "shelljs": "^0.7.0",
  61 + "stylelint": "^6.3.3",
  62 + "stylelint-config-yoho": "^1.2.2",
56 "webpack": "^1.13.0", 63 "webpack": "^1.13.0",
57 "webpack-dev-server": "^1.14.1", 64 "webpack-dev-server": "^1.14.1",
58 - "webpack-stream": "^3.1.0",  
59 - "shelljs": "^0.7.0" 65 + "webpack-stream": "^3.1.0"
60 } 66 }
61 } 67 }
  1 +let expect = require('expect.js');
  2 +let Timer = require('../../library/timer');
  3 +
  4 +
  5 +describe('/library/timer', function() {
  6 + it('延迟100ms,期望大于或等于100ms', function(done) {
  7 + let t = new Timer();
  8 + t.put('aa');
  9 + setTimeout(function() {
  10 + let time = t.put('aa');
  11 + expect(Math.round(time) >= 100).to.be.ok();
  12 + done();
  13 + }, 100);
  14 + });
  15 +});
  1 +require('./library/timer.test');