Coverage

78%
151
118
33

/blanket.js

76%
141
108
33
LineHitsSource
11var inBrowser = typeof window !== 'undefined' && this === window;
21var parseAndModify = (inBrowser ? window.falafel : require("falafel"));
3
41(inBrowser ? window : exports).blanket = (function(){
51 var linesToAddTracking = [
6 "ExpressionStatement",
7 "BreakStatement" ,
8 "ContinueStatement" ,
9 "VariableDeclaration",
10 "ReturnStatement" ,
11 "ThrowStatement" ,
12 "TryStatement" ,
13 "FunctionDeclaration" ,
14 "IfStatement" ,
15 "WhileStatement" ,
16 "DoWhileStatement" ,
17 "ForStatement" ,
18 "ForInStatement" ,
19 "SwitchStatement" ,
20 "WithStatement"
21 ],
22 linesToAddBrackets = [
23 "IfStatement" ,
24 "WhileStatement" ,
25 "DoWhileStatement" ,
26 "ForStatement" ,
27 "ForInStatement" ,
28 "WithStatement"
29 ],
30 __blanket,
31 copynumber = Math.floor(Math.random()*1000),
32 coverageInfo = {},options = {
33 reporter: null,
34 adapter:null,
35 filter: null,
36 customVariable: null,
37 loader: null,
38 ignoreScriptError: false,
39 existingRequireJS:false,
40 autoStart: false,
41 timeout: 180,
42 ignoreCors: false,
43 branchTracking: false,
44 sourceURL: false,
45 debug:false,
46 engineOnly:false,
47 testReadyCallback:null,
48 commonJS:false,
49 instrumentCache:false,
50 modulePattern: null
51 };
52
531 if (inBrowser && typeof window.blanket !== 'undefined'){
540 __blanket = window.blanket.noConflict();
55 }
56
571 _blanket = {
58 noConflict: function(){
590 if (__blanket){
600 return __blanket;
61 }
620 return _blanket;
63 },
64 _getCopyNumber: function(){
65 //internal method
66 //for differentiating between instances
670 return copynumber;
68 },
69 extend: function(obj) {
70 //borrowed from underscore
710 _blanket._extend(_blanket,obj);
72 },
73 _extend: function(dest,source){
740 if (source) {
750 for (var prop in source) {
760 if ( dest[prop] instanceof Object && typeof dest[prop] !== "function"){
770 _blanket._extend(dest[prop],source[prop]);
78 }else{
790 dest[prop] = source[prop];
80 }
81 }
82 }
83 },
84 getCovVar: function(){
8528 var opt = _blanket.options("customVariable");
8628 if (opt){
870 if (_blanket.options("debug")) {console.log("BLANKET-Using custom tracking variable:",opt);}
880 return inBrowser ? "window."+opt : opt;
89 }
9028 return inBrowser ? "window._$blanket" : "_$jscoverage";
91 },
92 options: function(key,value){
93327 if (typeof key !== "string"){
940 _blanket._extend(options,key);
95327 }else if (typeof value === 'undefined'){
96316 return options[key];
97 }else{
9811 options[key]=value;
99 }
100 },
101 instrument: function(config, next){
102 //check instrumented hash table,
103 //return instrumented code if available.
10410 var inFile = config.inputFile,
105 inFileName = config.inputFileName;
106 //check instrument cache
10710 if (_blanket.options("instrumentCache") && sessionStorage && sessionStorage.getItem("blanket_instrument_store-"+inFileName)){
1080 if (_blanket.options("debug")) {console.log("BLANKET-Reading instrumentation from cache: ",inFileName);}
1090 next(sessionStorage.getItem("blanket_instrument_store-"+inFileName));
110 }else{
11110 var sourceArray = _blanket._prepareSource(inFile);
11210 _blanket._trackingArraySetup=[];
11310 var instrumented = parseAndModify(inFile,{loc:true,comment:true}, _blanket._addTracking(inFileName));
1149 instrumented = _blanket._trackingSetup(inFileName,sourceArray)+instrumented;
1159 if (_blanket.options("sourceURL")){
1160 instrumented += "\n//@ sourceURL="+inFileName.replace("http://","");
117 }
1189 if (_blanket.options("debug")) {console.log("BLANKET-Instrumented file: ",inFileName);}
1199 if (_blanket.options("instrumentCache") && sessionStorage){
1200 if (_blanket.options("debug")) {console.log("BLANKET-Saving instrumentation to cache: ",inFileName);}
1210 sessionStorage.setItem("blanket_instrument_store-"+inFileName,instrumented);
122 }
1239 next(instrumented);
124 }
125 },
126 _trackingArraySetup: [],
127 _branchingArraySetup: [],
128 _prepareSource: function(source){
12911 return source.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/gm,"\n").split('\n');
130 },
131 _trackingSetup: function(filename,sourceArray){
13210 var branches = _blanket.options("branchTracking");
13310 var sourceString = sourceArray.join("',\n'");
13410 var intro = "";
13510 var covVar = _blanket.getCovVar();
136
13710 intro += "if (typeof "+covVar+" === 'undefined') "+covVar+" = {};\n";
13810 if (branches){
1395 intro += "var _$branchFcn=function(f,l,c,r){ ";
1405 intro += "if (!!r) { ";
1415 intro += covVar+"[f].branchData[l][c][0] = "+covVar+"[f].branchData[l][c][0] || [];";
1425 intro += covVar+"[f].branchData[l][c][0].push(r); }";
1435 intro += "else { ";
1445 intro += covVar+"[f].branchData[l][c][1] = "+covVar+"[f].branchData[l][c][1] || [];";
1455 intro += covVar+"[f].branchData[l][c][1].push(r); }";
1465 intro += "return r;};\n";
147 }
14810 intro += "if (typeof "+covVar+"['"+filename+"'] === 'undefined'){";
149
15010 intro += covVar+"['"+filename+"']=[];\n";
15110 if (branches){
1525 intro += covVar+"['"+filename+"'].branchData=[];\n";
153 }
15410 intro += covVar+"['"+filename+"'].source=['"+sourceString+"'];\n";
155 //initialize array values
15610 _blanket._trackingArraySetup.sort(function(a,b){
15728 return parseInt(a,10) > parseInt(b,10);
158 }).forEach(function(item){
15931 intro += covVar+"['"+filename+"']["+item+"]=0;\n";
160 });
16110 if (branches){
1625 _blanket._branchingArraySetup.sort(function(a,b){
16320 return a.line > b.line;
164 }).sort(function(a,b){
16533 return a.column > b.column;
166 }).forEach(function(item){
16725 if (item.file === filename){
1689 intro += "if (typeof "+ covVar+"['"+filename+"'].branchData["+item.line+"] === 'undefined'){\n";
1699 intro += covVar+"['"+filename+"'].branchData["+item.line+"]=[];\n";
1709 intro += "}";
1719 intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"] = [];\n";
1729 intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"].consequent = "+JSON.stringify(item.consequent)+";\n";
1739 intro += covVar+"['"+filename+"'].branchData["+item.line+"]["+item.column+"].alternate = "+JSON.stringify(item.alternate)+";\n";
174 }
175 });
176 }
17710 intro += "}";
178
17910 return intro;
180 },
181 _blockifyIf: function(node){
182246 if (linesToAddBrackets.indexOf(node.type) > -1){
1839 var bracketsExistObject = node.consequent || node.body;
1849 var bracketsExistAlt = node.alternate;
1859 if( bracketsExistAlt && bracketsExistAlt.type !== "BlockStatement") {
1862 bracketsExistAlt.update("{\n"+bracketsExistAlt.source()+"}\n");
187 }
1889 if( bracketsExistObject && bracketsExistObject.type !== "BlockStatement") {
1896 bracketsExistObject.update("{\n"+bracketsExistObject.source()+"}\n");
190 }
191 }
192 },
193 _trackBranch: function(node,filename){
194 //recursive on consequent and alternative
1959 var line = node.loc.start.line;
1969 var col = node.loc.start.column;
197
1989 _blanket._branchingArraySetup.push({
199 line: line,
200 column: col,
201 file:filename,
202 consequent: node.consequent.loc,
203 alternate: node.alternate.loc
204 });
205
2069 var source = node.source();
2079 var updated = "_$branchFcn"+
208 "('"+filename+"',"+line+","+col+","+source.slice(0,source.indexOf("?"))+
209 ")"+source.slice(source.indexOf("?"));
2109 node.update(updated);
211 },
212 _addTracking: function (filename) {
213 //falafel doesn't take a file name
214 //so we include the filename in a closure
215 //and return the function to falafel
21612 var covVar = _blanket.getCovVar();
217
21812 return function(node){
219246 _blanket._blockifyIf(node);
220
221246 if (linesToAddTracking.indexOf(node.type) > -1 && node.parent.type !== "LabeledStatement") {
22242 _blanket._checkDefs(node,filename);
22341 if (node.type === "VariableDeclaration" &&
224 (node.parent.type === "ForStatement" || node.parent.type === "ForInStatement")){
2251 return;
226 }
22740 if (node.loc && node.loc.start){
22840 node.update(covVar+"['"+filename+"']["+node.loc.start.line+"]++;\n"+node.source());
22940 _blanket._trackingArraySetup.push(node.loc.start.line);
230 }else{
231 //I don't think we can handle a node with no location
2320 throw new Error("The instrumenter encountered a node with no location: "+Object.keys(node));
233 }
234204 }else if (_blanket.options("branchTracking") && node.type === "ConditionalExpression"){
2359 _blanket._trackBranch(node,filename);
236 }
237 };
238 },
239 _checkDefs: function(node,filename){
240 // Make sure developers don't redefine window. if they do, inform them it is wrong.
24142 if (inBrowser){
2420 if (node.type === "VariableDeclaration" && node.declarations) {
2430 node.declarations.forEach(function(declaration) {
2440 if (declaration.id.name === "window") {
2450 throw new Error("Instrumentation error, you cannot redefine the 'window' variable in " + filename + ":" + node.loc.start.line);
246 }
247 });
248 }
2490 if (node.type === "FunctionDeclaration" && node.params) {
2500 node.params.forEach(function(param) {
2510 if (param.name === "window") {
2520 throw new Error("Instrumentation error, you cannot redefine the 'window' variable in " + filename + ":" + node.loc.start.line);
253 }
254 });
255 }
256 //Make sure developers don't redefine the coverage variable
2570 if (node.type === "ExpressionStatement" &&
258 node.expression && node.expression.left &&
259 node.expression.left.object && node.expression.left.property &&
260 node.expression.left.object.name +
261 "." + node.expression.left.property.name === _blanket.getCovVar()) {
2620 throw new Error("Instrumentation error, you cannot redefine the coverage variable in " + filename + ":" + node.loc.start.line);
263 }
264 }else{
265 //Make sure developers don't redefine the coverage variable in node
26642 if (node.type === "ExpressionStatement" &&
267 node.expression && node.expression.left &&
268 !node.expression.left.object && !node.expression.left.property &&
269 node.expression.left.name === _blanket.getCovVar()) {
2701 throw new Error("Instrumentation error, you cannot redefine the coverage variable in " + filename + ":" + node.loc.start.line);
271 }
272 }
273 },
274 setupCoverage: function(){
2751 coverageInfo.instrumentation = "blanket";
2761 coverageInfo.stats = {
277 "suites": 0,
278 "tests": 0,
279 "passes": 0,
280 "pending": 0,
281 "failures": 0,
282 "start": new Date()
283 };
284 },
285 _checkIfSetup: function(){
2864 if (!coverageInfo.stats){
2870 throw new Error("You must call blanket.setupCoverage() first.");
288 }
289 },
290 onTestStart: function(){
2911 if (_blanket.options("debug")) {console.log("BLANKET-Test event started");}
2921 this._checkIfSetup();
2931 coverageInfo.stats.tests++;
2941 coverageInfo.stats.pending++;
295 },
296 onTestDone: function(total,passed){
2971 this._checkIfSetup();
2981 if(passed === total){
2991 coverageInfo.stats.passes++;
300 }else{
3010 coverageInfo.stats.failures++;
302 }
3031 coverageInfo.stats.pending--;
304 },
305 onModuleStart: function(){
3061 this._checkIfSetup();
3071 coverageInfo.stats.suites++;
308 },
309 onTestsDone: function(){
3101 if (_blanket.options("debug")) {console.log("BLANKET-Test event done");}
3111 this._checkIfSetup();
3121 coverageInfo.stats.end = new Date();
313
3141 if (inBrowser){
3150 this.report(coverageInfo);
316 }else{
3171 if (!_blanket.options("branchTracking")){
3181 delete (inBrowser ? window : global)[_blanket.getCovVar()].branchFcn;
319 }
3201 this.options("reporter").call(this,coverageInfo);
321 }
322 }
323 };
3241 return _blanket;
325})();
326

testfile2

100%
1
1
0
LineHitsSource
15var a=3;if(a==1)a=2;else if(a==3){a=4;}result=a;

label_test

100%
5
5
0
LineHitsSource
11function aFunc(max) {
21var ret=0; rows: for (var i = 0; i < max--; i++) {
320 ret=i; if (i == 9) {
41 break rows;
5}
6}
71 return ret;}
8

branch_test_file2

100%
2
2
0
LineHitsSource
11function BRANCHTEST(x){
22return x === 1 ? true : false;
3}

branch_test_file3

100%
2
2
0
LineHitsSource
11function COMPLEXBRANCHTEST(x,y,z){
24return x === 1 ? true : y === 2 ? z === 3 ? true : false : false;
3}