[Pkg-javascript-commits] [node-jscoverage] 01/02: Imported Upstream version 0.5.0~rc2
Leo Iannacone
l3on-guest at moszumanska.debian.org
Sun May 11 16:07:06 UTC 2014
This is an automated email from the git hooks/post-receive script.
l3on-guest pushed a commit to branch master
in repository node-jscoverage.
commit afe2ee0dcba5086498e0d5dd8ea7ba7292d3b1a8
Author: Leo Iannacone <l3on at ubuntu.com>
Date: Sun May 11 17:42:26 2014 +0200
Imported Upstream version 0.5.0~rc2
---
.covignore | 3 +
.jshintrc | 44 ++++++
.npmignore | 1 +
LICENSE | 24 +++
README.md | 90 +++++++++++
bin/jscoverage | 85 +++++++++++
index.js | 262 +++++++++++++++++++++++++++++++
lib/instrument.js | 205 +++++++++++++++++++++++++
lib/jscoverage.js | 130 ++++++++++++++++
lib/patch.js | 227 +++++++++++++++++++++++++++
logo.png | Bin 0 -> 34900 bytes
package.json | 43 ++++++
reporter/detail.js | 140 +++++++++++++++++
reporter/html.js | 148 ++++++++++++++++++
reporter/summary.js | 47 ++++++
reporter/templates/coverage.ejs | 93 +++++++++++
reporter/templates/script.ejs | 34 +++++
reporter/templates/style.ejs | 330 ++++++++++++++++++++++++++++++++++++++++
test/abc.js | 51 +++++++
test/cde.js | 15 ++
test/dir/a/a2 | 3 +
test/dir/a/test.md | 1 +
test/dir/a1.js | 3 +
test/example.js | 120 +++++++++++++++
test/index.js | 165 ++++++++++++++++++++
test/jscoverage.js | 43 ++++++
test/patch.js | 30 ++++
test/reporter_detail.js | 38 +++++
28 files changed, 2375 insertions(+)
diff --git a/.covignore b/.covignore
new file mode 100644
index 0000000..62a0581
--- /dev/null
+++ b/.covignore
@@ -0,0 +1,3 @@
+/test/index.js
+/test/jscoverage.js
+/test/test.js
\ No newline at end of file
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..6c3451b
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,44 @@
+{
+ "predef": [
+ "document",
+ "module",
+ "require",
+ "__dirname",
+ "process",
+ "console",
+ "it",
+ "xit",
+ "describe",
+ "xdescribe",
+ "before",
+ "beforeEach",
+ "after",
+ "afterEach"
+ ],
+ "node": true,
+ "es5": true,
+ "bitwise": true,
+ "curly": true,
+ "eqeqeq": true,
+ "forin": false,
+ "immed": true,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "noempty": true,
+ "nonew": true,
+ "plusplus": false,
+ "undef": true,
+ "strict": false,
+ "trailing": false,
+ "globalstrict": true,
+ "nonstandard": true,
+ "white": false,
+ "indent": 2,
+ "expr": true,
+ "multistr": true,
+ "onevar": false,
+ "unused": "vars",
+ "sub": true,
+ "quoteMark": true
+}
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1 @@
+node_modules
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c6d4fc5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+(The MIT License)
+
+Copyright (c) 2011-2013 fish <zhengxinlin at gmail.com>
+
+Based on Uglify-js https://github.com/mishoo/UglifyJS
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9be7f78
--- /dev/null
+++ b/README.md
@@ -0,0 +1,90 @@
+jscoverage
+==========
+![logo](https://raw.github.com/fishbar/jscoverage/master/logo.png)
+
+jscoverage tool, both node.js or javascript support
+
+[![Build Status](https://travis-ci.org/fishbar/jscoverage.svg)](https://travis-ci.org/fishbar/jscoverage)
+
+
+### install
+
+```sh
+npm install jscoverage
+```
+
+### changelog
+
+from v0.5.0, jscoverage start using uglify2, and enhance the coverage range.
+now, jscoverage will find out which branch you missed!
+jscoverage can run with mocha, even with mocha's html-cov reporter.
+since mocha's html-cov dose not display branch cov info, jscoverage supply a new html-cov reporter
+
+### get source code
+
+```sh
+git clone git://github.com/fishbar/jscoverage.git
+```
+
+### using jscoverage with mocha
+
+let mocha load jscoverage using -r options, like:
+```sh
+mocha -r jscoverage --covignore .covignore --covout html --covinject true test
+```
+the case above, mocha do nothing with these options: --covignore , --covout --covinject
+but jscoverage can recognise them, all support options are here:
+
+ --covignore [filepath] # like gitignore, tell jscoverage to ignore these files
+
+ --covout [output report] # can be: summary, detail, json, html, lcov, default summary
+
+ --coverage [high,middle,low] # coverage level, default is: 90,70,30 , means 90% is high, 30% is low
+
+ --covinject [boolean] # switch if inject code for easytest(exports._get, _replace, _reset), default is false
+
+default jscoverage will search .covignore in the project root
+
+### using jscoverage as cli command
+
+```shell
+jscoverage
+# print help info
+jscoverage source.js
+# convert source.js to source-cov.js
+jscoverage source.js dest.js
+# convert source.js to dest.js
+jscoverage sourcedir destdir --exclude a.js,b.js,c.js,*.min.js
+# convert all files in sourcedir to destdir, exclude list will be ignored
+```
+jscoverage will copy exclude file from source dir to dest dir
+
+### using jscoverage programmatically
+
+comming soon
+
+### using inject api for node.js test
+
+```js
+var testMod = require('module/for/test.js');
+
+testMod._get('name');
+testMod._replace('name', value);
+testMod._reset();
+testMod._call();
+```
+### inline ignore annotation
+
+using bellow comment, jscoverage will ignore the following block/statement
+
+```js
+ /* @covignore */
+```
+
+### mocha global leaks detect
+
+The follow object will be detected, all of them are created by jscoverage.
+
+ * _$jscoverage
+ * _$jscmd
+
diff --git a/bin/jscoverage b/bin/jscoverage
new file mode 100755
index 0000000..25fa00c
--- /dev/null
+++ b/bin/jscoverage
@@ -0,0 +1,85 @@
+#!/usr/bin/env node
+/**
+ * [argv description]
+ * * @usage
+ * # cli command
+ *
+ * # using as a node module
+ */
+var argv = require('optimist').argv;
+var jscoverage = require('../index');
+var paths = argv._;
+var source = paths[0];
+var dest = paths[1];
+var exclude = argv.exclude;
+var fs = require('xfs');
+var path = require('path');
+
+if (!source) {
+ helpInfo();
+ process.exit();
+}
+var sourceStat;
+
+if (!dest) {
+ if (/\.\w+$/.test(source)) {
+ dest = source.replace(/(\.\w+)$/, '-cov$1');
+ } else {
+ dest = source.replace(/(\/|\\)$/, '') + '-cov';
+ }
+}
+
+if (exclude) {
+ exclude = exclude.split(',');
+ exclude.forEach(function (v, i, a) {
+ a[i] = new RegExp(v.replace(/\./, '\\.').replace(/\*/g, '.*'));
+ });
+}
+try {
+ sourceStat = fs.statSync(source);
+ if (sourceStat.isFile()) {
+ jscoverage.processFile(source, dest);
+ } else {
+ var count = 0;
+ fs.walk(source, function (err, file, done) {
+ if (err) {
+ console.error(err);
+ return done;
+ }
+ var flag = false;
+ if (exclude) {
+ exclude.forEach(function (v) {
+ if (v.test(file)) {
+ flag = true;
+ }
+ });
+ }
+ count ++;
+ var destFile = path.join(dest, file.substr(source.length));
+ if (flag) {
+ // copy exclude file
+ fs.save(destFile, fs.readFileSync(file));
+ } else {
+ jscoverage.processFile(file, destFile);
+ }
+ done();
+ }, function (err) {
+ if (err) {
+ return console.error(err);
+ }
+ console.log('process files:', count);
+ });
+ }
+} catch (e) {
+ console.log('source file not exist!', e);
+ process.exit();
+}
+
+function helpInfo() {
+ var help = [];
+ help.push('usage:');
+ help.push('\t> jscoverage source');
+ help.push('\t## this is equal : jscoverage source source-cov | jscoverage source.js source-cov.js');
+ help.push('\t> jscoverage source dest --exclude a,b,c,d');
+ console.log(help.join('\n'));
+}
diff --git a/index.js b/index.js
new file mode 100755
index 0000000..5ec4668
--- /dev/null
+++ b/index.js
@@ -0,0 +1,262 @@
+/*!
+ * jscoverage: index.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-03 15:20:13
+ * CopyRight 2014 (c) Fish And Other Contributors
+ *
+ */
+var fs = require('xfs');
+var path = require('path');
+var argv = require('optimist').argv;
+var patch = require('./lib/patch');
+var cmd = argv['$0'];
+var MODE_MOCHA = false;
+var FLAG_LOCK = false;
+if (/mocha/.test(cmd)) {
+ MODE_MOCHA = true;
+}
+
+if (MODE_MOCHA) {
+ prepareMocha();
+}
+/**
+ * prepare env for mocha test
+ * @covignore
+ */
+function prepareMocha() {
+ var covIgnore = argv.covignore;
+ var cwd = process.cwd();
+ var covlevel = argv.coverage;
+ if (covlevel) {
+ var tmp = covlevel.split(',');
+ covlevel = {
+ high: parseFloat(tmp[0], 10),
+ middle: parseFloat(tmp[1], 10),
+ low: parseFloat(tmp[2], 10)
+ };
+ } else {
+ covlevel = {
+ high: 0.9,
+ middle: 0.7,
+ low: 0.3
+ };
+ }
+ /**
+ * add after hook
+ * @return {[type]} [description]
+ */
+ process.nextTick(function () {
+ try {
+ after(function () {
+ if (FLAG_LOCK) {
+ return;
+ }
+ FLAG_LOCK = true;
+ if (typeof _$jscoverage === 'undefined') {
+ return;
+ }
+ try {
+ if (argv.covout === 'none') {
+ return;
+ }
+ if (!argv.covout) {
+ argv.covout = 'summary';
+ }
+ var reporter;
+ if (/^\w+$/.test(argv.covout)) {
+ reporter = require('./reporter/' + argv.covout);
+ } else {
+ reporter = require(argv.covout);
+ }
+ reporter.process(_$jscoverage, exports.coverageStats(), covlevel);
+ } catch (e) {
+ console.error('jscoverage reporter error', e, e.stack);
+ }
+ });
+ } catch (e) {
+ // do nothing
+ }
+ });
+ if (argv.covinject) {
+ patch.enableInject(true);
+ }
+ if (!covIgnore) {
+ try {
+ var stat = fs.statSync('.covignore');
+ stat && (covIgnore = '.covignore');
+ } catch (e) {
+ return;
+ }
+ }
+ try {
+ covIgnore = fs.readFileSync(covIgnore).toString().split(/\r?\n/g);
+ } catch (e) {
+ throw new Error('jscoverage loading covIgnore file error:' + covIgnore);
+ }
+ covIgnore.forEach(function (v, i, a) {
+ if (v.indexOf('/') === 0) {
+ v = '^' + cwd + v;
+ }
+ a[i] = new RegExp(v.replace(/\./g, '\\.').replace(/\*/g, '.*'));
+ });
+
+ patch.setCovIgnore(covIgnore);
+}
+
+var jscoverage = require('./lib/jscoverage');
+
+/**
+ * enableInject description
+ * @param {Boolean} true or false
+ */
+exports.enableInject = patch.enableInject;
+/**
+ * config the inject function names
+ * @param {Object} obj {get, replace, call, reset}
+ * @example
+ *
+ * jsc.config({get:'$get', replace:'$replace'});
+ *
+ * =====================
+ *
+ * testMod = require('testmodule');
+ * testMod.$get('name');
+ * testMod.$replace('name', obj);
+ */
+exports.config = function (obj) {
+ var inject_functions = patch.getInjectFunctions();
+ for (var i in obj) {
+ inject_functions[i] = obj[i];
+ }
+};
+/**
+ * process Code, inject the coverage code to the input Code string
+ * @param {String} filename jscoverage file flag
+ * @param {Code} content
+ * @return {Code} instrumented code
+ */
+exports.process = jscoverage.process;
+
+/**
+ * processFile, instrument singfile
+ * @sync
+ * @param {Path} source absolute Path
+ * @param {Path} dest absolute Path
+ * @param {Object} option [description]
+ */
+exports.processFile = function (source, dest, option) {
+ var content;
+ var stats;
+ // test source is file or dir, or not a file
+ try {
+ stats = fs.statSync(source);
+ if (stats.isDirectory()) {
+ throw new Error('path is dir');
+ } else if (!stats.isFile()) {
+ throw new Error('path is not a regular file');
+ }
+ } catch (e) {
+ throw new Error('source file error' + e);
+ }
+
+ fs.sync().mkdir(path.dirname(dest));
+
+ content = fs.readFileSync(source).toString();
+ content = content.toString();
+ content = this.process(source, content);
+ fs.writeFileSync(dest, content);
+};
+
+
+/**
+ * sum the coverage rate
+ * @public
+ */
+exports.coverageStats = function () {
+ var file;
+ var tmp;
+ var total;
+ var touched;
+ var n, len;
+ var stats = {};
+ var conds, condsMap, cond;
+ var line, start, offset;
+ if (typeof _$jscoverage === 'undefined') {
+ return;
+ }
+ for (var i in _$jscoverage) {
+ file = i;
+ tmp = _$jscoverage[i];
+ if (!tmp.length) {
+ continue;
+ }
+ total = touched = 0;
+ for (n = 0, len = tmp.length; n < len; n++) {
+ if (tmp[n] !== undefined) {
+ total ++;
+ if (tmp[n] > 0) {
+ touched ++;
+ }
+ }
+ }
+ conds = tmp.condition;
+ condsMap = {};
+ for (n in conds) {
+ if (conds[n] === 0) {
+ cond = n.split('_');
+ line = cond[0];
+ start = parseInt(cond[1], 10);
+ offset = parseInt(cond[2], 10);
+ if (!condsMap[line]) {
+ condsMap[line] = [];
+ }
+ condsMap[line].push([start, offset]);
+ } else {
+ touched ++;
+ }
+ total ++;
+ }
+ stats[file] = {
+ sloc: total,
+ hits: touched,
+ coverage: total ? touched / total : 0,
+ percent: total ? ((touched / total) * 100).toFixed(2) + '%' : '~',
+ condition: condsMap
+ };
+ }
+ return stats;
+};
+
+/**
+ * get lcov report
+ * @return {[type]} [description]
+ */
+exports.getLCOV = function () {
+ var tmp;
+ var total;
+ var touched;
+ var n, len;
+ var lcov = '';
+ if (typeof _$jscoverage === 'undefined') {
+ return;
+ }
+ Object.keys(_$jscoverage).forEach(function (file) {
+ lcov += 'SF:' + file + '\n';
+ tmp = _$jscoverage[file];
+ if (!tmp.length) {
+ return;
+ }
+ total = touched = 0;
+ for (n = 0, len = tmp.length; n < len; n++) {
+ if (tmp[n] !== undefined) {
+ lcov += 'DA:' + n + ',' + tmp[n] + '\n';
+ total ++;
+ if (tmp[n] > 0) {
+ touched++;
+ }
+ }
+ }
+ lcov += 'end_of_record\n';
+ });
+ return lcov;
+};
diff --git a/lib/instrument.js b/lib/instrument.js
new file mode 100755
index 0000000..7cb2e78
--- /dev/null
+++ b/lib/instrument.js
@@ -0,0 +1,205 @@
+/*!
+ * jscoverage: lib/instrument.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-03 15:20:13
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+/**
+ * instrument code
+ * @example
+ * var ist = new Instrument();
+ * var resCode = ist.process(str);
+ */
+var Uglify = require('uglify-js');
+
+function Instrument() {
+ /**
+ * filename needed
+ * @type {String}
+ */
+ this.filename = null;
+ /**
+ * store injected code
+ * @type {String}
+ */
+ this.code = null;
+ /**
+ * 储存line信息
+ * @type {Array}
+ */
+ this.lines = [];
+ /**
+ * 储存condition信息
+ * @type {Object}
+ */
+ this.conds = {};
+ /**
+ * source code in array
+ * @type {Array}
+ */
+ this.source = null;
+}
+
+Instrument.prototype = {
+ // 行类型
+ T_LINE: 'line',
+ T_COND: 'cond',
+ /**
+ * process code
+ * @public
+ * @param {String} code source code
+ * @return {String} injected code
+ */
+ process: function (filename, code) {
+ if (!filename) {
+ throw new Error('[jscoverage]instrument need filename!');
+ }
+
+ var ist = this;
+ // parse ast
+ var ast = Uglify.parse(code);
+
+ this.filename = filename;
+ this.source = code.split(/\r?\n/);
+
+ // init walker
+ var walker = new Uglify.TreeWalker(function (node) {
+ if (ist.checkIfIgnore(node, walker.stack)) {
+ return;
+ }
+
+ if (node instanceof Uglify.AST_Conditional) { // 三元判断
+ node.consequent = ist.inject('cond', node.consequent.start.line, node.consequent);
+ node.alternative = ist.inject('cond', node.alternative.start.line, node.alternative);
+ } else if (node.TYPE === 'Binary') {
+ if (!(node.left instanceof Uglify.AST_Constant)) {
+ node.left = ist.inject('cond', node.left.start.line, node.left);
+ }
+ if (!(node.right instanceof Uglify.AST_Constant)) {
+ node.right = ist.inject('cond', node.right.start.line, node.right);
+ }
+ }
+ var len = node.body ? node.body.length : 0;
+ if (len) {
+ var res = [];
+ var subNode;
+ for (var i = 0; i < len; i++) {
+ subNode = node.body[i];
+ if (ist.checkIfIgnore(subNode, walker.stack)) {
+ res.push(subNode);
+ continue;
+ }
+ if (subNode instanceof Uglify.AST_Statement) {
+ if (ist.ifExclude(subNode)) {
+ res.push(subNode);
+ continue;
+ }
+ res.push(ist.inject('line', subNode.start.line));
+ } else if (subNode instanceof Uglify.AST_Var) {
+ res.push(ist.inject('line', subNode.start.line));
+ }
+ res.push(subNode);
+ }
+ node.body = res;
+ }
+ });
+ // figure_out_scope
+ ast.figure_out_scope();
+ // walk process
+ ast.walk(walker);
+
+ var out = Uglify.OutputStream({
+ preserve_line : true,
+ comments: 'all',
+ beautify: true
+ });
+ // rebuild file
+ ast.print(out);
+ this.code = out.toString();
+ return this;
+ },
+ /**
+ * 注入覆盖率查询方法
+ * @private
+ * @param {String} type inject type, line | conds
+ * @param {Number} line line number
+ * @param {Object} expr any expression, or node, or statement
+ * @return {AST_Func} Object
+ */
+ inject: function (type, line, expr) {
+ var args = [];
+ if (type === this.T_LINE) {
+ this.lines.push(line);
+ args = [
+ new Uglify.AST_String({value: this.filename}),
+ new Uglify.AST_String({value: type}),
+ new Uglify.AST_Number({value: line})
+ ];
+ } else if (type === this.T_COND) {
+ var start = expr.start.col;
+ var offset = expr.end.endpos - expr.start.pos;
+ var key = line + '_' + start + '_' + offset; // 编码
+ this.conds[key] = 0;
+ args = [
+ new Uglify.AST_String({value: this.filename}),
+ new Uglify.AST_String({value: type}),
+ new Uglify.AST_String({value: key}),
+ expr
+ ];
+ }
+
+ var call = new Uglify.AST_Call({
+ expression: new Uglify.AST_SymbolRef({name: '_$jscmd'}),
+ //end: new Uglify.AST_
+ args: args
+ });
+
+ if (type === this.T_LINE) {
+ return new Uglify.AST_SimpleStatement({
+ body: call,
+ end: new Uglify.AST_Token({value: ';'})
+ });
+ } else {
+ return call;
+ }
+ },
+ /**
+ * check if need inject
+ * @param {AST_Node} node
+ * @return {Boolean}
+ */
+ ifExclude: function (node) {
+ if (node instanceof Uglify.AST_LoopControl) {
+ return false;
+ }
+ if (
+ node instanceof Uglify.AST_IterationStatement ||
+ node instanceof Uglify.AST_StatementWithBody ||
+ node instanceof Uglify.AST_Block
+ ) {
+ return true;
+ }
+ },
+ checkIfIgnore: function (node, stack) {
+ var cmt;
+ if (node.start && node.start.comments_before.length) {
+ cmt = node.start.comments_before[node.start.comments_before.length - 1];
+ if (/@covignore/.test(cmt.value) && !(node instanceof Uglify.AST_Toplevel)) {
+ node.__covignore = true;
+ }
+ }
+ if (node.__covignore) {
+ return true;
+ }
+ if (stack) {
+ for (var i = stack.length - 1; i > 0; i--) {
+ if (stack[i].__covignore) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+};
+
+module.exports = Instrument;
\ No newline at end of file
diff --git a/lib/jscoverage.js b/lib/jscoverage.js
new file mode 100755
index 0000000..422d1d2
--- /dev/null
+++ b/lib/jscoverage.js
@@ -0,0 +1,130 @@
+/*!
+ * jscoverage: lib/jscoverage.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-03 15:20:13
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+var Instrument = require('./instrument');
+
+/**
+ * do not exec this function
+ * the function body will insert into instrument files
+ *
+ * _$jscoverage = {
+ * filename : {
+ * line1: 0
+ * line2: 1
+ * line3: undefined
+ * ....
+ * source: [],
+ * condition: [
+ * line_start_offset
+ * ]
+ * }
+ * }
+ */
+function jscFunctionBody() {
+ // instrument by jscoverage, do not modifly this file
+ (function (file, lines, conds, source) {
+ var BASE;
+ if (typeof global === 'object') {
+ BASE = global;
+ } else if (typeof window === 'object') {
+ BASE = window;
+ } else {
+ throw new Error('[jscoverage] unknow ENV!');
+ }
+ if (BASE._$jscoverage) {
+ BASE._$jscmd(file, 'init', lines, conds, source);
+ return;
+ }
+ var cov = {};
+ /**
+ * jsc(file, 'init', lines, condtions)
+ * jsc(file, 'line', lineNum)
+ * jsc(file, 'cond', lineNum, expr, start, offset)
+ */
+ function jscmd(file, type, line, express, start, offset) {
+ var storage;
+ switch (type) {
+ case 'init':
+ storage = [];
+ for (var i = 0; i < line.length; i ++) {
+ storage[line[i]] = 0;
+ }
+ var condition = express;
+ var source = start;
+ storage.condition = condition;
+ storage.source = source;
+ cov[file] = storage;
+ break;
+ case 'line':
+ storage = cov[file];
+ storage[line] ++;
+ break;
+ case 'cond':
+ storage = cov[file];
+ storage.condition[line] ++;
+ return express;
+ }
+ }
+
+ BASE._$jscoverage = cov;
+ BASE._$jscmd = jscmd;
+ jscmd(file, 'init', lines, conds, source);
+ })('$file$', $lines$, $conds$, $source$);
+}
+/**
+ * gen coverage head
+ */
+function genCodeCoverage(instrObj) {
+ if (!instrObj) {
+ return '';
+ }
+ var code = [];
+ var filename = instrObj.filename;
+ // Fix windows path
+ filename = filename.replace(/\\/g, '/');
+ var lines = instrObj.lines;
+ var conditions = instrObj.conds;
+ var src = instrObj.source;
+ var jscfArray = jscFunctionBody.toString().split('\n');
+ jscfArray = jscfArray.slice(1, jscfArray.length - 1);
+ var ff = jscfArray.join('\n').replace(/(^|\n) {2}/g, '\n')
+ .replace(/\$(\w+)\$/g, function (m0, m1){
+ switch (m1) {
+ case 'file':
+ return filename;
+ case 'lines':
+ return JSON.stringify(lines);
+ case 'conds':
+ return JSON.stringify(conditions);
+ case 'source':
+ return JSON.stringify(src);
+ }
+ });
+ code.push(ff);
+ code.push(instrObj.code);
+ return code.join('\n');
+}
+
+exports.process = function (filename, content) {
+ if (!filename) {
+ throw new Error('jscoverage.process(filename, content), filename needed!');
+ }
+ filename = filename.replace(/\\/g, '/');
+ if (!content) {
+ return '';
+ }
+ var pwd = process.cwd();
+ var fname;
+ if (filename.indexOf(pwd) === 0) {
+ fname = filename.substr(pwd.length + 1);
+ } else {
+ fname = filename;
+ }
+ var instrObj;
+ var ist = new Instrument();
+ instrObj = ist.process(fname, content);
+ return genCodeCoverage(instrObj);
+};
diff --git a/lib/patch.js b/lib/patch.js
new file mode 100644
index 0000000..e438bbd
--- /dev/null
+++ b/lib/patch.js
@@ -0,0 +1,227 @@
+/*!
+ * jscoverage: lib/patch.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-03 15:20:13
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+var Module = require('module');
+var path = require('path');
+var fs = require('fs');
+var argv = require('optimist').argv;
+var jscoverage = require('./jscoverage');
+
+var covInject = false;
+var defaultCovIgnore = [
+ new RegExp('^' + process.cwd() + '/node_modules/'),
+ new RegExp('^' + process.cwd() + '/test/')
+];
+var covIgnore = defaultCovIgnore;
+
+var injectFunctions = {
+ get : '_get',
+ replace : '_replace',
+ call : '_call',
+ reset : '_reset',
+ test: '_test'
+};
+
+exports.getInjectFunctions = function () {
+ return injectFunctions;
+};
+
+exports.enableInject = function (bool) {
+ covInject = bool;
+};
+exports.setCovIgnore = function (ignore) {
+ covIgnore = ignore.concat(defaultCovIgnore);
+};
+/**
+ * do mock things here
+ * @covignore
+ */
+(function () {
+ if (Module.prototype.__jsc_patch__) {
+ return;
+ }
+ Module.prototype.__jsc_patch__ = true;
+ var origin_require = Module.prototype.require;
+ Module.prototype.require = function (filename) {
+ var needinject = covInject;
+ var ff = filename;
+ filename = Module._resolveFilename(filename, this);
+ var flagjsc = checkModule(filename);
+ if (typeof filename === 'object') {
+ filename = filename[0];
+ }
+
+ if (!flagjsc) {
+ return origin_require.call(this, filename);
+ }
+
+ var cachedModule = Module._cache[filename];
+ // take care of module cache
+ if (flagjsc && cachedModule && cachedModule.__coveraged__) {
+ return cachedModule.exports;
+ }
+ // console.log('jscoverage:', ff, 'cov', flagjsc, 'inject', needinject);
+ var module = new Module(filename, this);
+ try {
+ module.filename = filename;
+ module.paths = Module._nodeModulePaths(path.dirname(filename));
+ Module._extensions['.js'](module, filename, {
+ flagjsc : flagjsc,
+ needinject : needinject
+ });
+ module.__coveraged__ = flagjsc;
+ module.loaded = true;
+ Module._cache[filename] = module;
+ } catch (err) {
+ delete Module._cache[filename];
+ console.error(err.stack);
+ throw err;
+ }
+ return module.exports;
+ };
+ function stripBOM(content) {
+ // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
+ // because the buffer-to-string conversion in `fs.readFileSync()`
+ // translates it to FEFF, the UTF-16 BOM.
+ if (content.charCodeAt(0) === 0xFEFF) {
+ content = content.slice(1);
+ }
+ return content;
+ }
+ Module._extensions['.js'] = function (module, filename, status) {
+ var content = fs.readFileSync(filename, 'utf8');
+ var tmpFuncBody;
+ var injectFn = exports.getInjectFunctions();
+ // trim first line when script is a shell script
+ // content = content.replace(/^\#\![^\n]+\n/, '');
+ if (status && status.flagjsc) {
+ content = jscoverage.process(filename, content);
+ }
+ if (status && status.needinject) {
+ tmpFuncBody = injectFunctionBody.toString().replace(/\$\$(\w+)\$\$/g, function (m0, m1) {
+ return injectFunctions[m1];
+ });
+ tmpFuncBody = tmpFuncBody.split(/\n/);
+ content += '\n' + tmpFuncBody.slice(1, tmpFuncBody.length - 1).join('\n');
+ }
+ module._compile(stripBOM(content), filename);
+ };
+})();
+
+function checkModule(module) {
+
+ // native module
+ if (!/\//.test(module)) {
+ return false;
+ }
+ // modules in node_modules
+ var flagIgnore = false;
+ covIgnore.forEach(function (v) {
+ if (v.test(module)) {
+ flagIgnore = true;
+ }
+ });
+ return !flagIgnore;
+}
+
+/**
+ * do not exec this function
+ * @covignore
+ */
+function injectFunctionBody() {
+ (function (){
+ if (module.exports._i_n_j_e_c_t_e_d_) {
+ return;
+ }
+ if (module.exports.$$call$$ || module.exports.$$get$$ ||
+ module.exports.$$replace$$ || module.exports.$$reset$$) {
+ throw new Error("[jscoverage] jscoverage can not inject function for this module, because the function is exists! using jsc.config({inject:{}})");
+ }
+
+ var __r_e_p_l_a_c_e__ = {};
+ module.exports.$$replace$$ = function (name, obj) {
+ function stringify(obj) {
+ if (obj === null) {
+ return 'null';
+ }
+ if (obj === undefined){
+ return 'undefined';
+ }
+ if (!obj && isNaN(obj)){
+ return 'NaN';
+ }
+ if (typeof obj === 'string') {
+ return '"' + obj.replace(/"/g, '\\"') + '"';
+ }
+ if (typeof obj === 'number') {
+ return obj;
+ }
+ if (obj.constructor === Date) {
+ return 'new Date(' + obj.getTime() + ')';
+ }
+ if (obj.constructor === Function) {
+ return obj.toString();
+ }
+ if (obj.constructor === RegExp) {
+ return obj.toString();
+ }
+ var is_array = obj.constructor === Array ? true : false;
+ var res, i;
+ if (is_array) {
+ res = ['['];
+ for (i = 0; i < obj.length; i++) {
+ res.push(stringify(obj[i]));
+ res.push(',');
+ }
+ if (res[res.length - 1] === ',') {
+ res.pop();
+ }
+ res.push(']');
+ } else {
+ res = ['{'];
+ for (i in obj) {
+ res.push(i + ':' + stringify(obj[i]));
+ res.push(',');
+ }
+ if (res[res.length - 1] === ',')
+ res.pop();
+ res.push('}');
+ }
+ return res.join('');
+ }
+ if (!__r_e_p_l_a_c_e__.hasOwnProperty(name)) {
+ __r_e_p_l_a_c_e__[name] = eval(name);
+ }
+ eval(name + "=" + stringify(obj));
+ };
+ module.exports.$$reset$$ = function (name) {
+ var script;
+ if (name) {
+ script = 'if(__r_e_p_l_a_c_e__.hasOwnProperty("' + name + '"))' + name + ' = __r_e_p_l_a_c_e__["' + name + '"];';
+ } else {
+ script = 'for(var i in __r_e_p_l_a_c_e__){eval( i + " = __r_e_p_l_a_c_e__[\'" + i + "\'];");}';
+ }
+ eval(script);
+ };
+ module.exports.$$call$$ = module.exports.$$test$$ = function (func, args) {
+ var f, o;
+ if (func.match(/\\./)) {
+ func = func.split(".");
+ f = func[func.length - 1];
+ func.pop();
+ o = func.join(".");
+ } else {
+ f = func;
+ o = "this";
+ }
+ return eval(f + ".apply(" + o + "," + JSON.stringify(args) + ")");
+ };
+ module.exports.$$get$$ = function (objstr) {
+ return eval(objstr);
+ };
+ module.exports._i_n_j_e_c_t_e_d_ = true;
+})();
+}
diff --git a/logo.png b/logo.png
new file mode 100644
index 0000000..154ca97
Binary files /dev/null and b/logo.png differ
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..86c2f63
--- /dev/null
+++ b/package.json
@@ -0,0 +1,43 @@
+{
+ "name": "jscoverage",
+ "version": "0.5.0-rc2",
+ "description": "a javascript coverage tool, can be used in node dev, and browser side js dev",
+ "main": "index.js",
+ "bin": {
+ "jscoverage": "./bin/jscoverage"
+ },
+ "scripts": {
+ "test": "./node_modules/mocha/bin/_mocha -r ./index.js --covinject true test/"
+ },
+ "engines": {
+ "node" : ">=0.8"
+ },
+ "dependencies" : {
+ "uglify-js" : "2.4.13",
+ "optimist" : "0.3.1",
+ "xfs" : "0.1.7",
+ "ejs": "1.0.0"
+ },
+ "devDependencies" : {
+ "mocha" : "*",
+ "expect.js" : "*",
+ "xfs" : "*"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/fishbar/jscoverage.git"
+ },
+ "keywords": [
+ "jscoverage",
+ "node",
+ "javascript",
+ "coverage",
+ "dev",
+ "tool"
+ ],
+ "author": "fish <zhengxinlin at gmail.com>, kate.sf <kate.sf at taobao.com>",
+ "contributors":[
+ { "name": "christineRR", "email": "rongkunli1215 at gmail.com" }
+ ],
+ "license": "MIT"
+}
diff --git a/reporter/detail.js b/reporter/detail.js
new file mode 100644
index 0000000..c1909e6
--- /dev/null
+++ b/reporter/detail.js
@@ -0,0 +1,140 @@
+/*!
+ * jscoverage: reporter/detail.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-10 16:23:23
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+
+/**
+ * print detail coverage info in console
+ * @param {Object} _$jscoverage [description]
+ * @param {Object} stats [description]
+ * @param {Number} covlevel [description]
+ */
+exports.process = function (_$jscoverage, stats, covlevel) {
+ var file;
+ var tmp;
+ var source;
+ var lines;
+ var allcovered;
+ for (var i in _$jscoverage) {
+ file = i;
+ tmp = _$jscoverage[i];
+ if (typeof tmp === 'function' || tmp.length === undefined) {
+ continue;
+ }
+ source = tmp.source;
+ allcovered = true;
+ //console.log('[JSCOVERAGE]',file);
+ console.log('[UNCOVERED CODE]', file);
+ lines = [];
+ for (var n = 0, len = source.length; n < len ; n++) {
+ if (tmp[n] === 0) {
+ lines[n] = 1;
+ allcovered = false;
+ } else {
+ lines[n] = 0;
+ }
+ }
+ if (allcovered) {
+ console.log(colorful('\t100% covered', 'GREEN'));
+ } else {
+ printCoverageDetail(lines, source);
+ }
+ }
+};
+
+function processLinesMask(lines) {
+ function processLeft3(arr, offset) {
+ var prev1 = offset - 1;
+ var prev2 = offset - 2;
+ var prev3 = offset - 3;
+ if (prev1 < 0) {
+ return;
+ }
+ arr[prev1] = arr[prev1] === 1 ? arr[prev1] : 2;
+ if (prev2 < 0) {
+ return;
+ }
+ arr[prev2] = arr[prev2] === 1 ? arr[prev2] : 2;
+ if (prev3 < 0) {
+ return;
+ }
+ arr[prev3] = arr[prev3] ? arr[prev3] : 3;
+ }
+ function processRight3(arr, offset) {
+ var len = arr.length;
+ var next1 = offset;
+ var next2 = offset + 1;
+ var next3 = offset + 2;
+ if (next1 >= len || arr[next1] === 1) {
+ return;
+ }
+ arr[next1] = arr[next1] ? arr[next1] : 2;
+ if (next2 >= len || arr[next2] === 1) {
+ return;
+ }
+ arr[next2] = arr[next2] ? arr[next2] : 2;
+ if (next3 >= len || arr[next3] === 1) {
+ return;
+ }
+ arr[next3] = arr[next3] ? arr[next3] : 3;
+ }
+ var offset = 0;
+ var now;
+ var prev = 0;
+ while (offset < lines.length) {
+ now = lines[offset];
+ now = now !== 1 ? 0 : 1;
+ if (now !== prev) {
+ if (now === 1) {
+ processLeft3(lines, offset);
+ } else if (now === 0) {
+ processRight3(lines, offset);
+ }
+ }
+ prev = now;
+ offset ++;
+ }
+ return lines;
+}
+/**
+ * printCoverageDetail
+ * @param {Array} lines [true] 1 means no coveraged
+ * @return {}
+ */
+function printCoverageDetail(lines, source) {
+ var len = lines.length;
+ lines = processLinesMask(lines);
+ //console.log(lines);
+ for (var i = 1; i < len; i++) {
+ if (lines[i] !== 0) {
+ if (lines[i] === 3) {
+ console.log('......');
+ } else if (lines[i] === 2) {
+ echo(i, source[i - 1], false);
+ } else {
+ echo(i, source[i - 1], true);
+ }
+ }
+ }
+ function echo(lineNum, str, bool) {
+ console.log(colorful(lineNum, 'LINENUM') + '|' + colorful(str, bool ? 'YELLOW' : 'GREEN'));
+ }
+}
+/**
+ * colorful display
+ * @param {} str
+ * @param {} type
+ * @return {}
+ */
+function colorful(str, type) {
+ var head = '\x1B[', foot = '\x1B[0m';
+ var color = {
+ LINENUM : 36,
+ GREEN : 32,
+ YELLOW : 33,
+ RED : 31
+ };
+ return head + color[type] + 'm' + str + foot;
+}
\ No newline at end of file
diff --git a/reporter/html.js b/reporter/html.js
new file mode 100644
index 0000000..090622e
--- /dev/null
+++ b/reporter/html.js
@@ -0,0 +1,148 @@
+/*!
+ * jscoverage: reporter/html.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-10 16:23:23
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+
+/**
+ * Module dependencies.
+ */
+
+var fs = require('fs');
+var ejs = require('ejs');
+var path = require('path');
+
+/**
+ * Initialize a new `JsCoverage` reporter.
+ *
+ * @param {Runner} runner
+ * @param {Boolean} output
+ * @api public
+ */
+
+exports.process = function (_$jscoverage, stats, covlevel) {
+ var result = map(_$jscoverage, stats);
+ var file = __dirname + '/templates/coverage.ejs';
+ var str = fs.readFileSync(file, 'utf8').toString();
+
+ var html = ejs.render(str, {
+ debug: true,
+ cov: result,
+ coverageClass: function (n) {
+ n = n / 100;
+ if (n >= covlevel.high) {
+ return 'high';
+ }
+ if (n >= covlevel.middle) {
+ return 'medium';
+ }
+ if (n >= covlevel.low) {
+ return 'low';
+ }
+ return 'terrible';
+ },
+ filename: path.join(__dirname, './templates/cached.ejs')
+ });
+
+ fs.writeFileSync(process.cwd() + '/covreporter.html', html);
+ console.log('[REPORTER]: ', process.cwd() + '/covreporter.html');
+};
+
+/**
+ * Map jscoverage data to a JSON structure
+ * suitable for reporting.
+ *
+ * @param {Object} cov
+ * @return {Object}
+ * @api private
+ */
+
+function map(cov, stats) {
+ var ret = {
+ instrumentation: 'jscoverage',
+ sloc: 0,
+ hits: 0,
+ misses: 0,
+ coverage: 0,
+ files: []
+ };
+
+ for (var filename in cov) {
+ if (!cov(filename).length) {
+ continue;
+ }
+ var data = coverage(filename, cov[filename], stats[filename]);
+ ret.files.push(data);
+ ret.hits += data.hits;
+ ret.misses += data.misses;
+ ret.sloc += data.sloc;
+ }
+
+ ret.files.sort(function(a, b) {
+ return a.filename.localeCompare(b.filename);
+ });
+
+ if (ret.sloc > 0) {
+ ret.coverage = (ret.hits / ret.sloc) * 100;
+ }
+
+ return ret;
+};
+
+/**
+ * Map jscoverage data for a single source file
+ * to a JSON structure suitable for reporting.
+ *
+ * @param {String} filename name of the source file
+ * @param {Object} data jscoverage coverage data
+ * @return {Object}
+ * @api private
+ */
+
+function coverage(filename, data, stats) {
+ var ret = {
+ filename: filename,
+ coverage: stats.coverage * 100,
+ hits: stats.hits,
+ misses: stats.sloc - stats.hits,
+ sloc: stats.sloc,
+ source: []
+ };
+ data.source.forEach(function(line, num){
+ num++;
+ var conds = stats.condition[num];
+ var splits = [];
+ if (conds) {
+ conds.forEach(function (v) {
+ if (!splits[v[0]]) {
+ splits[v[0]] = {start:[], end:[]};
+ }
+ if (!splits[v[0] + v[1]]) {
+ splits[v[0] + v[1]] = {start: [], end: []};
+ }
+ splits[v[0]].start.push('<i class="cond-miss">');
+ splits[v[0] + v[1]].end.push('</i>');
+ });
+ var res = [];
+ var offset = 0;
+ splits.forEach(function (v, i) {
+ if (!v) {
+ return;
+ }
+ res.push(line.substr(offset, i - offset));
+ res.push(v.end.join(''));
+ res.push(v.start.join(''));
+ offset = i;
+ });
+ res.push(line.substr(offset));
+ line = res.join('');
+ }
+ ret.source[num] = {
+ source: line,
+ coverage: data[num] === undefined ? '' : data[num],
+ condition: conds && conds.length ? true : false
+ };
+ });
+ return ret;
+}
diff --git a/reporter/summary.js b/reporter/summary.js
new file mode 100644
index 0000000..83c0004
--- /dev/null
+++ b/reporter/summary.js
@@ -0,0 +1,47 @@
+/*!
+ * jscoverage: reporter/summary.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-10 16:23:23
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+
+/**
+ * summary reporter
+ * @param {[type]} _$jscoverage coverage object
+ * @param {[type]} stats coverage stats
+ * @param {Object} covmin coverage level {high, middle, low}
+ */
+exports.process = function (_$jscoverage, stats, covlevel) {
+ var arr = [];
+ Object.keys(stats).forEach(function (file) {
+ var msg = '[JSCOVERAGE] ' + file + ': hits[' + stats[file].hits + '], sloc[' + stats[file].sloc + '] coverage[' + stats[file].percent + ']';
+ var coverage = stats[file].coverage;
+ var type;
+ if (coverage < covlevel.low) {
+ type = 'RED';
+ } else if (coverage < covlevel.middle) {
+ type = 'YELLOW';
+ } else if (coverage < covlevel.high) {
+ type = null;
+ } else {
+ type = 'GREEN';
+ }
+ msg = colorful(msg, type);
+ arr.push(msg);
+ });
+ console.log('\n', arr.join('\n'));
+};
+
+function colorful(str, type) {
+ if (!type) {
+ return str;
+ }
+ var head = '\x1B[', foot = '\x1B[0m';
+ var color = {
+ LINENUM : 36,
+ GREEN : 32,
+ YELLOW : 33,
+ RED : 31
+ };
+ return head + color[type] + 'm' + str + foot;
+}
\ No newline at end of file
diff --git a/reporter/templates/coverage.ejs b/reporter/templates/coverage.ejs
new file mode 100644
index 0000000..14320bc
--- /dev/null
+++ b/reporter/templates/coverage.ejs
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Coverage</title>
+ <% include script %>
+ <% include style %>
+ </head>
+ <body>
+ <div id="coverage">
+ <h1 id="overview">jscoverage</h1>
+ <div id="menu">
+ <li><a href='#overview'>overview</a></li>
+ <% cov.files.forEach(function (file) { %>
+ <li>
+ <span class="cov <%= coverageClass(file.coverage) %>"> <%- file.coverage %></span>
+ <a href="#<%- file.filename %>">
+ <%
+ var segments = file.filename.split('/');
+ var basename = segments.pop();
+ if (segments.length) { %>
+ <span class="dirname"><%= segments.join('/') + '/' %></span>
+
+ <% } %>
+ <span class="basename"><%= basename %></span>
+ </a>
+ </li>
+ <% }); %>
+ </div>
+
+ <div id="stats" class="<%= coverageClass(cov.coverage) %>">
+ <div class="percentage"> <%= cov.coverage %>%</div>
+ <div class="sloc"> <%= cov.sloc %></div>
+ <div class="hits"> <%= cov.hits %></div>
+ <div class="misses"> <%= cov.misses %></div>
+ </div>
+ <div id="files">
+
+ <% cov.files.forEach(function (file) { %>
+ <div class="file">
+ <h2 id="<%- file.filename %>"> <%- file.filename %></h2>
+ <div id="stats" class="<%= coverageClass(file.coverage) %>">
+ <div class="percentage"> <%= file.coverage %>%</div>
+ <div class="sloc"><%- file.sloc %></div>
+ <div class="hits"><%- file.hits %></div>
+ <div class="misses"><%- file.misses %></div>
+ </div>
+ <table id="source">
+ <thead>
+ <tr>
+ <th>Line</th>
+ <th>Hits</th>
+ <th>Source</th>
+ </tr>
+ </thead>
+ <tbody>
+ <% var number; %>
+ <% var line; %>
+ <% for (number = 1; number < file.source.length; number ++) { %>
+ <% line = file.source[number];%>
+ <% if (line.condition) { %>
+ <tr class="miss">
+ <td class="line"> <%- number %></td>
+ <td class="hits"> <%- line.coverage %></td>
+ <td class="source"> <%- line.source %></td>
+ </tr>
+ <% } else if (line.coverage > 0) { %>
+ <tr class="hit">
+ <td class="line"> <%- number %></td>
+ <td class="hits"> <%- line.coverage %></td>
+ <td class="source"> <%- line.source %></td>
+ </tr>
+ <% } else if (0 === line.coverage) { %>
+ <tr class="miss">
+ <td class="line"> <%- number %></td>
+ <td class="hits"> 0 </td>
+ <td class="source"> <%- line.source %></td>
+ </tr>
+ <% } else { %>
+ <tr>
+ <td class="line"><%- number %></td>
+ <td class="hits"> </td>
+ <td class="source"><%- line.source %></td>
+ </tr>
+ <% } %>
+ <% } %>
+ </tbody>
+ </table>
+ </div>
+ <% }); %>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/reporter/templates/script.ejs b/reporter/templates/script.ejs
new file mode 100644
index 0000000..073cf79
--- /dev/null
+++ b/reporter/templates/script.ejs
@@ -0,0 +1,34 @@
+<script>
+
+headings = [];
+
+onload = function(){
+ headings = document.querySelectorAll('h2');
+};
+
+onscroll = function(e){
+ var heading = find(window.scrollY);
+ if (!heading) return;
+ var links = document.querySelectorAll('#menu a')
+ , link;
+
+ for (var i = 0, len = links.length; i < len; ++i) {
+ link = links[i];
+ link.className = link.getAttribute('href') == '#' + heading.id
+ ? 'active'
+ : '';
+ }
+};
+
+function find(y) {
+ var i = headings.length
+ , heading;
+
+ while (i--) {
+ heading = headings[i];
+ if (y >= heading.offsetTop) {
+ return heading;
+ }
+ }
+}
+</script>
diff --git a/reporter/templates/style.ejs b/reporter/templates/style.ejs
new file mode 100644
index 0000000..68f9ea3
--- /dev/null
+++ b/reporter/templates/style.ejs
@@ -0,0 +1,330 @@
+<style>
+
+body {
+ font: 14px/1.6 "Helvetica Neue", Helvetica, Arial, sans-serif;
+ margin: 0;
+ color: #5d7dad;
+ border-top: 2px solid #ddd;
+}
+
+#coverage {
+ padding: 60px;
+}
+
+h1 a {
+ color: inherit;
+ font-weight: inherit;
+}
+
+h1 a:hover {
+ text-decoration: none;
+}
+
+.onload h1 {
+ opacity: 1;
+}
+
+h2 {
+ width: 80%;
+ margin-top: 80px;
+ margin-bottom: 0;
+ font-weight: 100;
+ letter-spacing: 1px;
+ border-bottom: 1px solid #eee;
+}
+
+a {
+ color: #8A6343;
+ font-weight: bold;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+ul {
+ margin-top: 20px;
+ padding: 0 15px;
+ width: 100%;
+}
+
+ul li {
+ float: left;
+ width: 40%;
+ margin-top: 5px;
+ margin-right: 60px;
+ list-style: none;
+ border-bottom: 1px solid #eee;
+ padding: 5px 0;
+ font-size: 12px;
+}
+
+ul::after {
+ content: '.';
+ height: 0;
+ display: block;
+ visibility: hidden;
+ clear: both;
+}
+
+code {
+ font: 12px monaco, monospace;
+}
+
+pre {
+ margin: 30px;
+ padding: 30px;
+ border: 1px solid #eee;
+ border-bottom-color: #ddd;
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+ border-radius: 2px;
+ -webkit-box-shadow: inset 0 0 10px #eee;
+ -moz-box-shadow: inset 0 0 10px #eee;
+ box-shadow: inset 0 0 10px #eee;
+ overflow-x: auto;
+}
+
+img {
+ margin: 30px;
+ padding: 1px;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ -webkit-box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
+ -moz-box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
+ box-shadow: 0 3px 10px #dedede, 0 1px 5px #888;
+ max-width: 100%;
+}
+
+footer {
+ background: #eee;
+ width: 100%;
+ padding: 50px 0;
+ text-align: right;
+ border-top: 1px solid #ddd;
+}
+
+footer span {
+ display: block;
+ margin-right: 30px;
+ color: #888;
+ font-size: 12px;
+}
+
+#menu {
+ position: fixed;
+ font-size: 12px;
+ overflow-y: auto;
+ top: 0;
+ right: 0;
+ margin: 0;
+ height: 100%;
+ padding: 15px 0;
+ text-align: left;
+ border-left: 1px solid #eee;
+ -moz-box-shadow: 0 0 2px #888
+ , inset 5px 0 20px rgba(0,0,0,.5)
+ , inset 5px 0 3px rgba(0,0,0,.3);
+ -webkit-box-shadow: 0 0 2px #888
+ , inset 5px 0 20px rgba(0,0,0,.5)
+ , inset 5px 0 3px rgba(0,0,0,.3);
+ box-shadow: 0 0 2px #888
+ , inset 5px 0 20px rgba(0,0,0,.5)
+ , inset 5px 0 3px rgba(0,0,0,.3);
+ -webkit-font-smoothing: antialiased;
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGYAAABmCAMAAAAOARRQAAABelBMVEUjJSU6OzshIyM5OjoqKy02NjgsLS01NTYjJCUzNTUgISMlJSc0NTUvMDA6PDwlJyg1NjYoKis2NjYrLS02ODkpKyw0NDYrLC04ODovLzA4Ojo0NDUtLy86OjwjIyU4OTosLS82ODgtLS8hIyQvMTEnKCooKSsrKy0qLCwkJSUnKCkrLCwpKiwwMjIxMzMqLC0tLS0pKissLC00NTYwMDIwMTQpKysoKSovMDEtLzA2OTkxMzUrKywvLy8qKyszNTY5OzsqKiw6OjswMDExNDUoKiozNDUvMDIyNDY1Njg2Njk5OTozMzU0NjY4ODkiIyUiIyQ4OTkuMDEmKCowMjQwMTErLS4qKywwMTMhIiMpKiopKy0tLjAkJScxNDQvLzExNDYyNDQmKCk [...]
+}
+
+#menu::after {
+ display: block;
+ content: '';
+ padding-top: 80px;
+}
+
+#logo {
+ position: fixed;
+ bottom: 10px;
+ right: 10px;
+ background: rgba(255,255,255,.1);
+ font-size: 11px;
+ display: block;
+ width: 20px;
+ height: 20px;
+ line-height: 20px;
+ text-align: center;
+ -webkit-border-radius: 20px;
+ -moz-border-radius: 20px;
+ border-radius: 20px;
+ -webkit-box-shadow: 0 0 3px rgba(0,0,0,.2);
+ -moz-box-shadow: 0 0 3px rgba(0,0,0,.2);
+ box-shadow: 0 0 3px rgba(0,0,0,.2);
+ color: inherit;
+}
+
+#menu li a {
+ display: block;
+ color: white;
+ padding: 0 35px 0 25px;
+ -webkit-transition: background 300ms;
+ -moz-transition: background 300ms;
+}
+
+#menu li {
+ position: relative;
+ list-style: none;
+}
+
+#menu a:hover,
+#menu a.active {
+ text-decoration: none;
+ background: rgba(255,255,255,.1);
+}
+
+#menu li:hover .cov {
+ opacity: 1;
+}
+
+#menu li .dirname {
+ opacity: .60;
+ padding-right: 2px;
+}
+
+#menu li .basename {
+ opacity: 1;
+}
+
+#menu .cov {
+ background: rgba(200,200,200, 1);
+ position: absolute;
+ top: 0;
+ right: 8px;
+ font-size: 9px;
+ opacity: .6;
+ text-align: left;
+ width: 17px;
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+ padding: 1px 2px;
+ text-align: center;
+}
+
+#stats:nth-child(2n) {
+ display: inline-block;
+ margin-top: 15px;
+ border: 1px solid #eee;
+ padding: 10px;
+ -webkit-box-shadow: inset 0 0 2px #eee;
+ -moz-box-shadow: inset 0 0 2px #eee;
+ box-shadow: inset 0 0 2px #eee;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+#stats div {
+ float: left;
+ padding: 0 5px;
+}
+
+#stats::after {
+ display: block;
+ content: '';
+ clear: both;
+}
+
+#stats .sloc::after {
+ content: ' SLOC';
+ color: #b6b6b6;
+}
+
+#stats .percentage::after {
+ content: ' coverage';
+ color: #b6b6b6;
+}
+
+#stats .hits {
+ display: none;
+}
+#stats .misses:after {
+ content: ' MISSES';
+ color: #b6b6b6;
+}
+
+.high {
+ color: #00d4b4;
+}
+.medium {
+ color: #e87d0d;
+}
+.low {
+ color: #d4081a;
+}
+.terrible {
+ color: #d4081a;
+ font-weight: bold;
+}
+
+table {
+ width: 80%;
+ margin-top: 10px;
+ border-collapse: collapse;
+ border: 1px solid #cbcbcb;
+ color: #363636;
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+}
+
+table thead {
+ display: none;
+}
+
+table td.line,
+table td.hits {
+ width: 20px;
+ background: #eaeaea;
+ text-align: center;
+ font-size: 11px;
+ padding: 0 10px;
+ color: #949494;
+}
+
+table td.hits {
+ width: 10px;
+ padding: 2px 5px;
+ color: rgba(0,0,0,.2);
+ background: #f0f0f0;
+}
+
+tr.miss td.line,
+tr.miss td.hits {
+ background: #e6c3c7;
+}
+
+tr.miss td {
+ background: #f8d5d8;
+}
+
+td.source {
+ padding-left: 15px;
+ line-height: 15px;
+ white-space: pre;
+ font: 12px monaco, monospace;
+}
+
+code .comment { color: #ddd }
+code .init { color: #2F6FAD }
+code .string { color: #5890AD }
+code .keyword { color: #8A6343 }
+code .number { color: #2F6FAD }
+
+i.cond-miss{
+ background-color:#ff6464;
+ font-style:normal;
+ border-radius: 4px;
+ padding: 0px 2px;
+}
+</style>
\ No newline at end of file
diff --git a/test/abc.js b/test/abc.js
new file mode 100644
index 0000000..a0616de
--- /dev/null
+++ b/test/abc.js
@@ -0,0 +1,51 @@
+/*!
+ * jscoverage: test/abc.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-10 16:23:23
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+var cde = require('./cde');
+var a = 1;
+var b = 2;
+var c = 3;
+var d;
+var e = a > 1 ? 1 : 2;
+
+var reset = {
+ abc:function () {}
+};
+
+function abc() {
+ var tmp = a + b;
+ var t = 1;
+ // test require ok
+ cde.a();
+ // test switch coverage
+ testSwitch('first');
+ /* @covignore */
+ testSwitch('second');
+ testSwitch();
+ return tmp + c;
+}
+
+function testSwitch(act) {
+ var res = [
+ 'a',
+ 'b',
+ 'c'
+ ];
+ var tmp;
+ switch (act) {
+ case 'first' :
+ tmp = res[0];
+ break;
+ case 'second' :
+ tmp = res[1];
+ break;
+ default:
+ tmp = res.join(',');
+ }
+ return tmp;
+}
+abc();
+exports.abc = abc;
diff --git a/test/cde.js b/test/cde.js
new file mode 100644
index 0000000..e8b9cff
--- /dev/null
+++ b/test/cde.js
@@ -0,0 +1,15 @@
+/*!
+ * jscoverage: test/cde.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-10 16:23:23
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+function a(){
+ var a = 1;
+ var b = 2;
+ if (a || b > a) {
+ console.log(a);
+ }
+ return a+b;
+}
+exports.a = a;
\ No newline at end of file
diff --git a/test/dir/a/a2 b/test/dir/a/a2
new file mode 100644
index 0000000..c68ea98
--- /dev/null
+++ b/test/dir/a/a2
@@ -0,0 +1,3 @@
+function a2(){
+ return "this is function a2";
+}
\ No newline at end of file
diff --git a/test/dir/a/test.md b/test/dir/a/test.md
new file mode 100644
index 0000000..3f751ae
--- /dev/null
+++ b/test/dir/a/test.md
@@ -0,0 +1 @@
+var str = "this is a test file";
diff --git a/test/dir/a1.js b/test/dir/a1.js
new file mode 100644
index 0000000..8e5c44f
--- /dev/null
+++ b/test/dir/a1.js
@@ -0,0 +1,3 @@
+function a1(){
+ return "this is function a1";
+}
\ No newline at end of file
diff --git a/test/example.js b/test/example.js
new file mode 100644
index 0000000..58799e9
--- /dev/null
+++ b/test/example.js
@@ -0,0 +1,120 @@
+/*!
+ * jscoverage: example.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-03 15:20:13
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+var expect = require('expect.js');
+
+exports.testTryCatch = function (a) {
+ try {
+ return a.run();
+ } catch (e) {
+ return 'catch error';
+ }
+};
+
+exports.testIfElse = function (a, b, c, d) {
+ if (a > 0 && b > 0) {
+ return 'ab';
+ } else if (c || d) {
+ return 'cd';
+ } else {
+ return 'unknow';
+ }
+};
+
+exports.testCondition = function (a, b ,c) {
+ return a || b > c ? '1' : '2';
+};
+
+exports.testWhile = function () {
+ var a = 0;
+ var res = '';
+ while(a < 2) {
+ res += 'a';
+ a ++;
+ }
+ var b = 0;
+ do {
+ res += 'b';
+ b ++;
+ } while (b < 2);
+ return res;
+};
+
+
+describe('example.js', function () {
+ it('test try catch', function () {
+ expect(exports.testTryCatch({})).to.be.match(/catch\ error/);
+ expect(exports.testTryCatch({run: function () {return 'run';}})).to.be('run');
+ });
+
+ it('test if else', function () {
+ expect(exports.testIfElse(1, 0)).to.be('unknow');
+ expect(exports.testIfElse(1, 1)).to.be('ab');
+ expect(exports.testIfElse(0, 0, 1, 0)).to.be('cd');
+ });
+
+ it('test condition', function () {
+ expect(exports.testCondition(1)).to.be('1');
+ expect(exports.testCondition(0, 2, 1)).to.be('1');
+ expect(exports.testCondition(0, 0, 0)).to.be('2');
+ });
+
+ it('test while', function () {
+ expect(exports.testWhile()).to.be('aabb');
+ });
+
+ it('test switch', function () {
+ expect(exports.testSwitch('a')).to.be('a');
+ expect(exports.testSwitch('b')).to.be('b');
+ expect(exports.testSwitch('c')).to.be('c');
+ expect(exports.testSwitch('0')).to.be('d');
+ });
+
+ it('test for', function () {
+ expect(exports.testFor()).to.be(1);
+ });
+
+ it('test binary', function () {
+ expect(exports.testBinary('a')).to.be('a');
+ expect(exports.testBinary(null, 'b')).to.be('b');
+ expect(exports.testBinary(null, null, null, 'b')).to.be('b');
+ expect(exports.testBinary(null, null, 'b')).to.be('b');
+ });
+});
+
+exports.testSwitch = function (a) {
+ var res = null;
+ switch (a) {
+ case 'a':
+ return 'a';
+ case 'b':
+ return 'b';
+ case 'c':
+ res = 'c';
+ break;
+ default:
+ res = 'd';
+ }
+ return res;
+};
+
+exports.testFor = function () {
+ var a = 0;
+ for (var i = 0; i < 3; i++) {
+ if (i > 1) {
+ a ++;
+ }
+ }
+ return a;
+};
+exports.testBinary = function (a, b, c, d) {
+ return a || b || c || d;
+};
+// anonymous function
+(function(){
+ var a = 1;
+ console.log('this line should covered');
+})();
\ No newline at end of file
diff --git a/test/index.js b/test/index.js
new file mode 100644
index 0000000..008d703
--- /dev/null
+++ b/test/index.js
@@ -0,0 +1,165 @@
+/*!
+ * jscoverage: test/index.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-03 15:20:13
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+var fs = require('xfs');
+var path = require('path');
+var expect = require('expect.js');
+var Module = require('module');
+var index = require('../index');
+
+index.config({
+ get: '_get',
+ call: '$$call',
+ replace: '_replace',
+ reset: '_reset'
+});
+
+var abc = require('./abc');
+
+describe("index.js", function () {
+ describe("exports.processFile", function () {
+ it('should return an jsc convert file', function () {
+ var source = path.join(__dirname, './abc.js');
+ var dest = path.join(__dirname, './abc.cov.js');
+ index.processFile(source, dest);
+ expect(fs.existsSync(dest)).to.be(true);
+ expect(fs.readFileSync(dest)).to.match(/_\$jscoverage/);
+ fs.unlinkSync(dest);
+ });
+ it('should throw error when path not file', function () {
+ var source = path.join(__dirname, './dir');
+ var dest = path.join(__dirname, './abc.cov.js');
+ var err;
+ try {
+ index.processFile(source, dest);
+ } catch (e) {
+ err = e;
+ }
+ expect(err.message).to.match(/path is dir/);
+ });
+ it('should throw error when path is a socket file', function (done) {
+ var net = require('net');
+ var serv = net.createServer(function(client){});
+ var ff = path.join(__dirname, './dir/a.sock');
+ serv.listen(ff, function (err) {
+ try {
+ index.processFile(ff, path.join(__dirname, './dir/sock-cov'));
+ } catch (e) {
+ expect(e.message).to.match(/not a regular file/);
+ }
+ serv.close(done);
+ });
+ });
+ /*
+ it('should return an jsc convert dir', function (done) {
+ var source = path.join(__dirname, './dir');
+ var dest = path.join(__dirname, './dir-cov');
+ index.processFile(source, dest);
+ expect(fs.existsSync(dest)).to.be(true);
+ expect(fs.readFileSync(dest + '/a1.js')).to.match(/_\$jscoverage/);
+ expect(fs.readFileSync(dest + '/a/a2')).to.match(/_\$jscoverage/);
+ fs.rmdir(dest, function () {
+ done();
+ });
+ });
+ it('should ignore exclude', function (done) {
+ var source = path.join(__dirname, './dir');
+ var dest = path.join(__dirname, './dir-cov');
+ index.processFile(source, dest, ['a2', /\.md$/i]);
+ expect(fs.existsSync(dest + '/a/a2')).to.not.ok();
+ fs.rmdir(dest, function () {
+ done();
+ });
+ });
+ */
+ it('should throw error when source and dest not currect', function () {
+ try {
+ index.processFile();
+ } catch (e) {
+ expect(e.message).to.match(/path must be a string/);
+ }
+ });
+ it('should throw error when source and dest not currect', function () {
+ function _empty() {
+ index.processFile('./abc', '.abc.cov');
+ }
+ expect(_empty).to.throwException(/no such file or directory/);
+ });
+ it('should throw error when source and dest not currect', function () {
+ function _empty() {
+ index.processFile(path.join(__dirname, './abdc.js'), '/tmp/abc.cov.js');
+ }
+ expect(_empty).to.throwException(/no such file or directory/);
+ });
+ });
+
+
+
+ describe('test Module.extension[".js"]', function () {
+ it('should return a function', function (done) {
+ var module = {
+ _compile: function (content, filename) {
+ var ff = new Function ('require', 'module', 'exports', '__dirname', '__filename', content + ';return module.exports;');
+ var module = {exports: {}};
+ var mo = ff(require, module, module.exports, __dirname, filename);
+ mo._replace('d', [
+ undefined,
+ null,
+ 1,
+ NaN,
+ 'string',
+ [1, 2, 3],
+ {'abc': [1, 2, 3]},
+ /a\\\\b/g
+ ]);
+ var res = mo._get('d');
+ expect(res[0]).to.be(undefined);
+ expect(res[1]).to.be(null);
+ expect(res[2]).to.be(1);
+ expect(isNaN(res[3])).to.be.ok();
+ expect(res[7].test('a\\\\bc')).to.be.ok();
+ mo._reset();
+ expect(mo._get('d')).to.be(undefined);
+ done();
+ }
+ };
+ Module._extensions['.js'](module, path.join(__dirname, './abc.js'), {
+ needjsc : true,
+ flagjsc : true,
+ needinject : true
+ });
+ });
+ it('should return a function', function (done) {
+ var module = {
+ _compile: function (content, filename) {
+ var ff = new Function ('require', 'module', 'exports', '__dirname', '__filename', content + ';return module.exports;');
+ var module = {exports: {}};
+ var mo = ff(require, module, module.exports, __dirname, filename);
+ mo._replace('d', {});
+ var res = mo._get('d');
+ expect(res).to.be.eql({});
+ mo._reset();
+ expect(mo._get('d')).to.be(undefined);
+ done();
+ }
+ };
+ Module._extensions['.js'](module, path.join(__dirname, './abc.js'), {
+ needjsc : true,
+ flagjsc : true,
+ needinject : true
+ });
+ });
+ });
+
+ describe('getLCOV', function () {
+ it('should be ok', function () {
+ var res = index.getLCOV();
+ expect(res).to.match(/end_of_record/);
+ expect(res).to.match(/SF:/);
+ expect(res).to.match(/DA:\d+,\d+/);
+ });
+ });
+});
diff --git a/test/jscoverage.js b/test/jscoverage.js
new file mode 100644
index 0000000..df7964a
--- /dev/null
+++ b/test/jscoverage.js
@@ -0,0 +1,43 @@
+/*!
+ * jscoverage: test/jscoverage.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-08 19:32:38
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+var jscoverage = require('../lib/jscoverage');
+var expect = require('expect.js');
+jscoverage.__coveraged__ = false;
+jscoverage = require('../lib/jscoverage');
+
+var func = jscoverage._get('jscFunctionBody').toString().split(/\n/);
+func.shift();
+func.pop();
+
+var wrapperGlobal = eval('(function(){ var global = {}; var $lines$ = [1]; var $conds$ = {"1_1_1": 0}; var $source$=["var a = a ? 1: 0;"];' + func.join('\n') + ' return global;})');
+
+var _global = wrapperGlobal();
+
+describe('lib/jscoverage.js', function(){
+ it('jscFunctionBody shoud be ok', function(){
+ // mark line
+ _global._$jscmd('$file$', 'line', 1);
+ // mark cond
+ _global._$jscmd('$file$', 'cond', '1_1_1', '');
+ expect(_global._$jscoverage['$file$'][1]).to.be(1);
+ expect(_global._$jscoverage['$file$'].condition['1_1_1']).to.eql(1);
+ });
+
+ it('shoud return "" when content empty', function () {
+ var res = jscoverage.process('abc', '');
+ expect(res).to.be('');
+ });
+ it('shoud throw error when filename empty', function () {
+ var err;
+ try {
+ jscoverage.process(null, '');
+ } catch (e) {
+ err = e;
+ }
+ expect(err.message).to.match(/filename needed!/);
+ });
+});
\ No newline at end of file
diff --git a/test/patch.js b/test/patch.js
new file mode 100644
index 0000000..d732ef2
--- /dev/null
+++ b/test/patch.js
@@ -0,0 +1,30 @@
+/*!
+ * jscoverage: test/patch.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-10 16:23:23
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+var patch = require('../lib/patch');
+var expect = require('expect.js');
+var checkModule = patch._get('checkModule');
+
+describe('patch.js', function () {
+ describe('checkModule()', function () {
+ it('should return false when native module', function () {
+ expect(checkModule('fs')).to.be(false);
+ });
+ it('should return false when /node_modules/ module', function () {
+ expect(checkModule(process.cwd() + '/node_modules/fs')).to.be(false);
+ });
+ it('should return false when native module', function () {
+ expect(checkModule('fs')).to.be(false);
+ });
+ it('should return true when ignore no match', function () {
+ expect(checkModule('/abc/def')).to.be(true);
+ });
+ it('should return true when ignore no match', function () {
+ patch.setCovIgnore([/\/tet\/a.js/]);
+ expect(checkModule('/tet/a.js')).to.be(false);
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/reporter_detail.js b/test/reporter_detail.js
new file mode 100644
index 0000000..2107d57
--- /dev/null
+++ b/test/reporter_detail.js
@@ -0,0 +1,38 @@
+/*!
+ * jscoverage: test/reporter_detail.js
+ * Authors : fish <zhengxinlin at gmail.com> (https://github.com/fishbar)
+ * Create : 2014-04-10 16:23:23
+ * CopyRight 2014 (c) Fish And Other Contributors
+ */
+var expect = require('expect.js');
+var testMod = require('../reporter/detail');
+describe('exports.processLinesMask', function () {
+ it('should be ok when test1', function () {
+ var process = testMod._get('processLinesMask');
+ var input = [0, 0, 0, 1, 1, 0, 0, 1, 0, 0];
+ var result = [3, 2, 2, 1, 1, 2, 2, 1, 2, 2];
+ expect(process(input)).to.be.eql(result);
+ });
+ it('should be ok when test2', function () {
+ var process = testMod._get('processLinesMask');
+ var input = [0, 0, 1, 1, 0, 0, 1, 0, 1];
+ var result = [2, 2, 1, 1, 2, 2, 1, 2, 1];
+ expect(process(input)).to.be.eql(result);
+ });
+ it('should be ok when test3', function () {
+ var process = testMod._get('processLinesMask');
+ var input = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0];
+ var result = [2, 2, 1, 2, 2, 3, 0, 0, 0, 3, 2, 2, 1, 2];
+ expect(process(input)).to.be.eql(result);
+ });
+ it('should be ok when test4', function () {
+ var process = testMod._get('processLinesMask');
+ var input = [0];
+ var result = [0];
+ expect(process(input)).to.be.eql(result);
+ });
+
+ it('should be ok', function () {
+ testMod.process(_$jscoverage, {}, {high: 90, middle: 70, low: 20});
+ });
+});
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-jscoverage.git
More information about the Pkg-javascript-commits
mailing list