Socket
Socket
Sign inDemoInstall

coffee-coverage

Package Overview
Dependencies
3
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.2.0 to 0.3.0

6

CHANGELOG.md
# coffee-coverage Changelog
### v0.3.0
- Add support for dynamically compiling .coffee and ._coffee files on the fly. (Special thanks
to [sivad77](https://github.com/sivad77) for the suggestion.)
- Experimental support for [streamlinejs](https://github.com/Sage/streamlinejs).
### v0.2.0

@@ -4,0 +10,0 @@

276

lib/coffeeCoverage.js
// Generated by CoffeeScript 1.6.3
(function() {
var COFFEE_EXTENSION, CoverageError, JS_EXTENSION, abbreviatedPath, coffeeScript, debug, defaultOptions, defaults, endsWith, events, fs, mkdirs, path, pkginfo, statFile, stripLeadingDot, util, _ref,
var CoverageError, EXTENSIONS, StringStream, abbreviatedPath, coffeeScript, debug, defaultOptions, defaults, endsWith, events, fs, getRelativeFilename, mkdirs, path, pkginfo, startsWith, statFile, stripLeadingDotOrSlash, util, _ref,
__hasProp = {}.hasOwnProperty,

@@ -20,3 +20,3 @@ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },

_ref = require('./helpers'), endsWith = _ref.endsWith, defaults = _ref.defaults, abbreviatedPath = _ref.abbreviatedPath, mkdirs = _ref.mkdirs, stripLeadingDot = _ref.stripLeadingDot, statFile = _ref.statFile;
_ref = require('./helpers'), startsWith = _ref.startsWith, endsWith = _ref.endsWith, defaults = _ref.defaults, abbreviatedPath = _ref.abbreviatedPath, mkdirs = _ref.mkdirs, stripLeadingDotOrSlash = _ref.stripLeadingDotOrSlash, statFile = _ref.statFile;

@@ -27,6 +27,11 @@ pkginfo = require('pkginfo')(module, 'version', 'author', 'contributors');

COFFEE_EXTENSION = ".coffee";
EXTENSIONS = {
".coffee": {
js_extension: ".js"
},
"._coffee": {
js_extension: "._js"
}
};
JS_EXTENSION = ".js";
CoverageError = (function(_super) {

@@ -46,2 +51,15 @@ __extends(CoverageError, _super);

StringStream = (function() {
function StringStream() {
this.data = "";
}
StringStream.prototype.write = function(data) {
return this.data += data;
};
return StringStream;
})();
defaultOptions = {

@@ -54,2 +72,78 @@ coverageVar: '_$jscoverage',

getRelativeFilename = function(basePath, fileName) {
var relativeFileName;
relativeFileName = path.resolve(fileName);
if ((basePath != null) && startsWith(relativeFileName, basePath)) {
relativeFileName = relativeFileName.slice(basePath.length);
}
return relativeFileName;
};
exports.register = function(options) {
var basePath, coverage, excludeFile, initStream, instrumentFile, module, origCoffeeHandler, origStreamineCoffeeHandler, streamline_js;
coverage = new exports.CoverageInstrumentor(options);
module = require('module');
if (options.basePath) {
basePath = path.resolve(options.basePath);
if (options.initAll) {
initStream = new StringStream();
coverage.instrumentDirectory(options.basePath, null, {
exclude: options.exclude,
recursive: true,
initFileStream: initStream
});
eval(initStream.data);
}
}
excludeFile = function(fileName) {
var excludePath, excluded, relativeFilename, _i, _len, _ref1, _ref2;
excluded = false;
if (basePath) {
relativeFilename = getRelativeFilename(basePath, fileName);
if (relativeFilename === fileName) {
excluded = true;
}
}
if (_ref1 = !path.extname(fileName), __indexOf.call(Object.keys(EXTENSIONS), _ref1) >= 0) {
excluded = true;
}
_ref2 = options.exclude || [];
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
excludePath = _ref2[_i];
if (startsWith(fileName, excludePath)) {
excluded = true;
}
}
return excluded;
};
instrumentFile = function(fileName) {
var content, coverageFileName, instrumented;
content = fs.readFileSync(fileName, 'utf8');
coverageFileName = getRelativeFilename(basePath, fileName);
instrumented = coverage.instrumentCoffee(coverageFileName, content, options);
return instrumented.init + instrumented.js;
};
origCoffeeHandler = require.extensions[".coffee"];
require.extensions[".coffee"] = function(module, fileName) {
if (excludeFile(fileName)) {
return origCoffeeHandler.call(this, module, fileName);
}
return module._compile(instrumentFile(fileName), fileName);
};
if (options.streamlinejs) {
streamline_js = require.extensions["._js"];
if (streamline_js) {
origStreamineCoffeeHandler = require.extensions["._coffee"];
return require.extensions["._coffee"] = function(module, fileName) {
var compiled;
if (excludeFile(fileName)) {
return origStreamineCoffeeHandler.call(this, module, fileName);
}
compiled = instrumentFile(fileName);
return streamline_js(module, fileName, compiled, null);
};
}
}
};
exports.CoverageInstrumentor = (function(_super) {

@@ -132,9 +226,28 @@ var fileToLines, fixLocationData, generateUniqueName, nodeType, toQuotedString, validateSrcDest, writeToFile;

CoverageInstrumentor.prototype.getOutputFileName = function(fileName) {
var coffee_extension, ext, outFile;
if (fileName == null) {
return null;
}
outFile = fileName;
for (coffee_extension in EXTENSIONS) {
ext = EXTENSIONS[coffee_extension];
if (endsWith(fileName.toLowerCase(), coffee_extension)) {
outFile = fileName.slice(0, +(-(coffee_extension.length + 1)) + 1 || 9e9) + ext.js_extension;
break;
}
}
return outFile;
};
CoverageInstrumentor.prototype.instrumentDirectory = function(sourceDirectory, outDirectory, options) {
var answer, file, inst, outDirectoryStat, outFile, outputDirectoryExists, sourceDirectoryMode, sourceFile, sourceStat, _i, _len, _ref1;
var answer, coffee_extension, exclude, file, inst, instrumentOptions, outDirectoryStat, outFile, outputDirectoryExists, processed, relativePath, skip, sourceDirectoryMode, sourceFile, sourceStat, _i, _j, _len, _len1, _ref1, _ref2;
if (options == null) {
options = {};
}
sourceDirectory = path.resolve(sourceDirectory);
this.emit("instrumentingDirectory", sourceDirectory, outDirectory);
options = Object.create(options);
options.usedFileNames = options.usedFileNames || [];
options.basePath = options.basePath ? path.resolve(options.basePath) : sourceDirectory;
answer = {

@@ -148,29 +261,57 @@ lines: 0

}
if (!endsWith(outDirectory, path.sep)) {
outDirectory += path.sep;
sourceDirectoryMode = (statFile(sourceDirectory)).mode;
if (outDirectory) {
if (!endsWith(outDirectory, path.sep)) {
outDirectory += path.sep;
}
outDirectoryStat = statFile(outDirectory);
outputDirectoryExists = !!outDirectoryStat;
}
outDirectoryStat = statFile(outDirectory);
outputDirectoryExists = !!outDirectoryStat;
sourceDirectoryMode = (statFile(sourceDirectory)).mode;
_ref1 = fs.readdirSync(sourceDirectory);
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
file = _ref1[_i];
skip = false;
if (__indexOf.call(options.exclude, file) >= 0) {
skip = true;
}
sourceFile = sourceDirectory + file;
relativePath = getRelativeFilename(options.basePath, sourceFile);
if (relativePath !== sourceFile) {
_ref2 = options.exclude;
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
exclude = _ref2[_j];
if (startsWith(relativePath, exclude)) {
skip = true;
}
}
}
if (skip) {
this.emit("skip", sourceDirectory + file);
continue;
}
sourceFile = sourceDirectory + file;
outFile = outDirectory + file;
outFile = outDirectory ? outDirectory + file : null;
sourceStat = statFile(sourceFile);
if (endsWith(file.toLowerCase(), COFFEE_EXTENSION) && sourceStat.isFile()) {
if (!outputDirectoryExists) {
mkdirs(outDirectory, sourceDirectoryMode);
outputDirectoryExists = true;
}
outFile = outFile.slice(0, +(-(COFFEE_EXTENSION.length + 1)) + 1 || 9e9) + JS_EXTENSION;
inst = this.instrumentFile(sourceFile, outFile, options);
answer.lines += inst.lines;
} else if (options.recursive && sourceStat.isDirectory()) {
if (options.recursive && sourceStat.isDirectory()) {
inst = this.instrumentDirectory(sourceFile, outFile, options);
answer.lines += inst.lines;
} else {
processed = false;
for (coffee_extension in EXTENSIONS) {
if (coffee_extension === '._coffee') {
continue;
}
if (endsWith(file.toLowerCase(), coffee_extension) && sourceStat.isFile()) {
if ((outDirectory != null) && !outputDirectoryExists) {
mkdirs(outDirectory, sourceDirectoryMode);
outputDirectoryExists = true;
}
outFile = this.getOutputFileName(outFile);
instrumentOptions = Object.create(options);
instrumentOptions.fileName = relativePath;
inst = this.instrumentFile(sourceFile, outFile, instrumentOptions);
answer.lines += inst.lines;
processed = true;
break;
}
}
}

@@ -182,26 +323,13 @@ }

CoverageInstrumentor.prototype.instrumentFile = function(sourceFile, outFile, options) {
var answer, data, filename, _ref1;
var answer, data;
if (outFile == null) {
outFile = null;
}
if (options == null) {
options = {};
}
this.emit("instrumentingFile", sourceFile, outFile);
validateSrcDest(sourceFile, outFile);
switch (options.path) {
case 'relative':
filename = stripLeadingDot(sourceFile);
break;
case 'abbr':
filename = abbreviatedPath(stripLeadingDot(sourceFile));
break;
default:
filename = path.basename(sourceFile);
}
if (options.usedFileNames && __indexOf.call(options.usedFileNames, filename) >= 0) {
filename = generateUniqueName(options.usedFileNames, filename);
}
if (options.usedFileNames) {
options.usedFileNames.push(filename);
}
data = fs.readFileSync(sourceFile, 'utf8');
answer = this.instrumentCoffee(filename, data);
if ((_ref1 = options.initFileStream) != null) {
_ref1.write(answer.init);
}
answer = this.instrumentCoffee(options.fileName || sourceFile, data, options);
if (outFile) {

@@ -227,7 +355,32 @@ writeToFile(outFile, answer.init + answer.js);

CoverageInstrumentor.prototype.instrumentCoffee = function(fileName, fileData) {
var ast, fileToInstrumentLines, index, init, instrumentTree, instrumentedLines, js, line, lineNumber, options, quotedFilename, _i, _j, _len, _len1;
options = this.options;
quotedFilename = toQuotedString(fileName);
ast = coffeeScript.nodes(fileData);
CoverageInstrumentor.prototype.instrumentCoffee = function(fileName, fileData, options) {
var answer, ast, err, fileToInstrumentLines, index, init, instrumentTree, instrumentedLines, js, line, lineNumber, origFileName, quotedFileName, _i, _j, _len, _len1, _ref1,
_this = this;
if (options == null) {
options = {};
}
origFileName = fileName;
switch (options.path) {
case 'relative':
fileName = stripLeadingDotOrSlash(fileName);
break;
case 'abbr':
fileName = abbreviatedPath(stripLeadingDotOrSlash(fileName));
break;
default:
fileName = path.basename(fileName);
}
if (options.usedfileNames) {
if (__indexOf.call(options.usedfileNames, fileName) >= 0) {
fileName = generateUniqueName(options.usedfileNames, fileName);
}
options.usedfileNames.push(fileName);
}
quotedFileName = toQuotedString(fileName);
try {
ast = coffeeScript.nodes(fileData);
} catch (_error) {
err = _error;
throw new CoverageError("Could not parse " + fileName + ": " + err.stack);
}
instrumentedLines = [];

@@ -243,3 +396,3 @@ instrumentTree = function(node, parent, depth) {

debug("Examining l:" + (node.locationData.first_line + 1) + " d:" + depth + " " + (nodeType(node)));
if (nodeType(node) !== "Block") {
if ((nodeType(node) !== "Block") || node.coffeeCoverageDoNotInstrument) {
if (nodeType(node) === "If" && node.isChain) {

@@ -270,3 +423,3 @@ debug(" Disabling chaining for if statement");

instrumentedLines.push(line);
instrumentedLine = coffeeScript.nodes("" + options.coverageVar + "[" + quotedFilename + "][" + line + "]++");
instrumentedLine = coffeeScript.nodes("" + _this.options.coverageVar + "[" + quotedFileName + "][" + line + "]++");
fixLocationData(instrumentedLine, line);

@@ -283,9 +436,9 @@ children.splice(childIndex, 0, instrumentedLine);

instrumentTree(ast);
init = "if (typeof " + options.coverageVar + " === 'undefined') " + options.coverageVar + " = {};\nif ((typeof global !== 'undefined') && (typeof global." + options.coverageVar + " === 'undefined')) {\n global." + options.coverageVar + " = " + options.coverageVar + "\n} else if ((typeof window !== 'undefined') && (typeof window." + options.coverageVar + " === 'undefined')) {\n window." + options.coverageVar + " = " + options.coverageVar + "\n}\nif (! " + options.coverageVar + "[" + quotedFilename + "]) {\n " + options.coverageVar + "[" + quotedFilename + "] = [];\n";
init = "if (typeof " + this.options.coverageVar + " === 'undefined') " + this.options.coverageVar + " = {};\nif ((typeof global !== 'undefined') && (typeof global." + this.options.coverageVar + " === 'undefined')) {\n global." + this.options.coverageVar + " = " + this.options.coverageVar + "\n} else if ((typeof window !== 'undefined') && (typeof window." + this.options.coverageVar + " === 'undefined')) {\n window." + this.options.coverageVar + " = " + this.options.coverageVar + "\n}\nif (! " + this.options.coverageVar + "[" + quotedFileName + "]) {\n " + this.options.coverageVar + "[" + quotedFileName + "] = [];\n";
for (_i = 0, _len = instrumentedLines.length; _i < _len; _i++) {
lineNumber = instrumentedLines[_i];
init += " " + options.coverageVar + "[" + quotedFilename + "][" + lineNumber + "] = 0;\n";
init += " " + this.options.coverageVar + "[" + quotedFileName + "][" + lineNumber + "] = 0;\n";
}
init += "}\n\n";
init += "" + options.coverageVar + "[" + quotedFilename + "].source = [";
init += "" + this.options.coverageVar + "[" + quotedFileName + "].source = [";
fileToInstrumentLines = fileToLines(fileData);

@@ -300,6 +453,14 @@ for (index = _j = 0, _len1 = fileToInstrumentLines.length; _j < _len1; index = ++_j) {

init += "];\n\n";
js = ast.compile({
bare: options.bare
});
return {
try {
js = ast.compile({
bare: options.bare
});
} catch (_error) {
err = _error;
throw new CoverageError("Could not compile " + fileName + " after annotating: " + err.stack);
}
if ((_ref1 = options.initFileStream) != null) {
_ref1.write(init);
}
answer = {
init: init,

@@ -309,2 +470,3 @@ js: js,

};
return answer;
};

@@ -311,0 +473,0 @@

// Generated by CoffeeScript 1.6.3
(function() {
var CoverageInstrumentor, fs, parseArgs, path, printHelp, stripLeadingDot, version, _ref;
var CoverageInstrumentor, fs, mkdirs, parseArgs, path, printHelp, stripLeadingDotOrSlash, version, _ref, _ref1;

@@ -13,3 +13,3 @@ fs = require('fs');

stripLeadingDot = require('./helpers').stripLeadingDot;
_ref1 = require('./helpers'), stripLeadingDotOrSlash = _ref1.stripLeadingDotOrSlash, mkdirs = _ref1.mkdirs;

@@ -75,3 +75,3 @@ printHelp = function() {

exports.main = function(args) {
var coverageInstrumentor, err, options, result, _ref1;
var coverageInstrumentor, err, options, result, _ref2;
try {

@@ -87,17 +87,18 @@ options = parseArgs(args.slice(2));

coverageInstrumentor.on("instrumentingFile", function(sourceFile, outFile) {
return console.log(" " + (stripLeadingDot(sourceFile)) + " to " + (stripLeadingDot(outFile)));
return console.log(" " + (stripLeadingDotOrSlash(sourceFile)) + " to " + (stripLeadingDotOrSlash(outFile)));
});
coverageInstrumentor.on("instrumentingDirectory", function(sourceDir, outDir) {
return console.log("Instrumenting directory: " + (stripLeadingDot(sourceDir)) + " to " + (stripLeadingDot(outDir)));
return console.log("Instrumenting directory: " + (stripLeadingDotOrSlash(sourceDir)) + " to " + (stripLeadingDotOrSlash(outDir)));
});
coverageInstrumentor.on("skip", function(file) {
return console.log(" Skipping: " + (stripLeadingDot(file)));
return console.log(" Skipping: " + (stripLeadingDotOrSlash(file)));
});
}
if (options.initfile) {
mkdirs(path.dirname(options.initfile));
options.initFileStream = fs.createWriteStream(options.initfile);
}
result = coverageInstrumentor.instrument(options.src, options.dest, options);
if ((_ref1 = options.initFileStream) != null) {
_ref1.end();
if ((_ref2 = options.initFileStream) != null) {
_ref2.end();
}

@@ -104,0 +105,0 @@ return console.log("Annotated " + result.lines + " lines.");

@@ -33,4 +33,4 @@ // Generated by CoffeeScript 1.6.3

exports.stripLeadingDot = function(pathName) {
return pathName.replace(/^\.\//, "");
exports.stripLeadingDotOrSlash = function(pathName) {
return pathName.replace(/^\//, "").replace(/^\.\//, "");
};

@@ -37,0 +37,0 @@

@@ -5,3 +5,3 @@ {

"keywords": ["javascript", "coffeescript", "coverage", "code coverage", "test"],
"version": "0.2.0",
"version": "0.3.0",
"author": "Benbria (http://www.benbria.com/)",

@@ -8,0 +8,0 @@ "contributors": [

@@ -71,24 +71,52 @@ Benbria CoffeeCoverage

At Benbria, we use coffeeCoverage to find out how much coverage we get from our unit tests. Our
process works like this; first we make a copy of our code base:
### Dynamic Compilation
cd project
mkdir /tmp/coverage
tar -cf - . | (cd /tmp/coverage && tar -xf -)
cd /tmp/coverage
There are two ways to use coffeeCoverage as part of your unit tests. First, if you run your
tests directly on your .coffee files, you can register coffeeCoverage to dynamically compile
.coffee (and even ._coffee if you're using [streamlinejs](https://github.com/Sage/streamlinejs))
files. For example, create a "register-handlers.js":
Then we instrument it in-place. We exclude the "test" directory, since we don't want coverage of
our actual test code:
# If you're using with streamline, you *must* register streamline first:
require('streamline').register({});
coffeeCoverage --initfile init.js --exclude node_modules,.git,test --path abbr . .
# Register coffee-coverage if coverage is enabled.
if(process.env.COVERAGE) {
require('coffee-coverage').register({
path: 'abbr',
basePath: __dirname,
exclude: ['/test', '/node_modules', '/.git'],
initAll: true,
streamlinejs: true
});
}
We don't have to delete the .coffee files, since when we `require 'foo'`, node will preferentially
load the foo.js file over the foo.coffee file. coffeeCoverage nicely gives us the number of lines
it instrumented - this is handy, because if we never `require` a given file from our tests, it
won't show up in the mocha report.
Note we set the "basePath" to the root of our project. This can be a path which is relative to
`__dirname` (e.g. `__dirname + "/.."`).
Next we run our tests:
Note that streamline support is "experimental" right now (i.e. it might break at any moment
because we're using undocumented features in streamlinejs) so to turn it on, you have to
explicitly pass 'streamlinejs: true' as an option.
mocha --require init.js --reporter html-cov --compilers coffee:coffee-script test/*Test.coffee
Then, run your tests:
COVERAGE=true mocha --require register-handlers.js --reporter html-cov ...
### Static Compilation
Alternatively, you can use coffeeCoverage to statically compile your code with instrumentation:
# Compile everything except the test directory with coffeeCoverage
coffeeCoverage --initfile ./lib/init.js --exclude test --path abbr ./src ./lib
# Compile the test directory with regular coffee-script
coffee -o ./lib/test ./src/test
This also writes an "lib/init.js" which initializes all the execution counts to 0. This is handy,
because otherwise if we never `require` a given module, that module's counts won't show up at all
in the code coverage report, which might overly inflate our code coverage percentage. Next we run
our tests:
mocha --require ./lib/init.js --reporter html-cov ./lib/test/*
Static compilation does not currently support streamline.
Some Weirdness with Line Numbers

@@ -118,2 +146,30 @@ --------------------------------

Also, it's worth noting a minor difference in the way coffee-coverage compiles statements. The
following coffee code:
if x
a()
else if y
b()
Would normally compile to:
if(x) {
a();
} else if(y) {
b();
}
coffeeCoverage will instead compile this to:
if(x) {
a();
} else {
if(y) {
b();
}
}
because otherwise it would be unable to annotate the `if(y)` statement.
Detailed Usage

@@ -120,0 +176,0 @@ --------------

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc