1 | 1 | var parseAndModify = (typeof exports === 'undefined' ? window.falafel : require("/Users/alexsouthern/Dropbox/blanket/src/./lib/falafel").falafel); |
2 | | |
3 | 1 | function copy(o){ |
4 | 0 | var _copy = Object.create( Object.getPrototypeOf(o) ); |
5 | 0 | var propNames = Object.getOwnPropertyNames(o); |
6 | | |
7 | 0 | propNames.forEach(function(name){ |
8 | 0 | var desc = Object.getOwnPropertyDescriptor(o, name); |
9 | 0 | Object.defineProperty(_copy, name, desc); |
10 | | }); |
11 | | |
12 | 0 | return _copy; |
13 | | } |
14 | | |
15 | 1 | (typeof exports === 'undefined' ? window : exports).blanket = (function(){ |
16 | 1 | var linesToAddTracking = [ |
17 | | "ExpressionStatement", |
18 | | "LabeledStatement" , |
19 | | "BreakStatement" , |
20 | | "ContinueStatement" , |
21 | | "VariableDeclaration", |
22 | | "ReturnStatement" , |
23 | | "ThrowStatement" , |
24 | | "TryStatement" , |
25 | | "FunctionDeclaration" , |
26 | | "IfStatement" , |
27 | | "WhileStatement" , |
28 | | "DoWhileStatement" , |
29 | | "ForStatement" , |
30 | | "ForInStatement" , |
31 | | "SwitchStatement" , |
32 | | "WithStatement" |
33 | | ], |
34 | | linesToAddBrackets = [ |
35 | | "IfStatement" , |
36 | | |
37 | | "WhileStatement" , |
38 | | "DoWhileStatement" , |
39 | | "ForStatement" , |
40 | | "ForInStatement" , |
41 | | "WithStatement" |
42 | | ], |
43 | | covVar = (typeof window === 'undefined' ? "_$jscoverage" : "window._$blanket" ), |
44 | | reporter,instrumentFilter,adapter, |
45 | | coverageInfo = {},existingRequireJS=false, |
46 | | blanket = { |
47 | | _reporter: null, |
48 | | extend: function(obj) { |
49 | | //borrowed from underscore |
50 | 0 | blanket._extend(blanket,obj); |
51 | | }, |
52 | | _extend: function(dest,source){ |
53 | 0 | if (source) { |
54 | 0 | for (var prop in source) { |
55 | 0 | if (dest[prop]){ |
56 | 0 | blanket._extend(dest[prop],source[prop]); |
57 | | }else{ |
58 | 0 | dest[prop] = source[prop]; |
59 | | } |
60 | | } |
61 | | } |
62 | | }, |
63 | | setExistingRequirejs: function(exists){ |
64 | 2 | existingRequireJS = exists || false; |
65 | | }, |
66 | | getExistingRequirejs: function(){ |
67 | 3 | return existingRequireJS; |
68 | | }, |
69 | | setFilter: function(filter){ |
70 | 3 | instrumentFilter = filter; |
71 | | }, |
72 | | getFilter: function(){ |
73 | 4 | return instrumentFilter; |
74 | | }, |
75 | | setReporter: function(reporterFcn){ |
76 | 4 | reporter = reporterFcn; |
77 | | }, |
78 | | getReporter: function(){ |
79 | 4 | return reporter; |
80 | | }, |
81 | | instrument: function(config, next){ |
82 | 2 | var inFile = config.inputFile, |
83 | | inFileName = config.inputFileName; |
84 | 2 | var sourceArray = this._prepareSource(inFile); |
85 | 2 | blanket._trackingArraySetup=[]; |
86 | 2 | var instrumented = parseAndModify(inFile,{loc:true,comment:true}, this._addTracking,inFileName); |
87 | 2 | instrumented = this._trackingSetup(inFileName,sourceArray)+instrumented; |
88 | 2 | next(instrumented); |
89 | | }, |
90 | | _trackingArraySetup: [], |
91 | | _prepareSource: function(source){ |
92 | 3 | return source.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/(\r\n|\n|\r)/gm,"\n").split('\n'); |
93 | | }, |
94 | | _trackingSetup: function(filename,sourceArray){ |
95 | | |
96 | 3 | var sourceString = sourceArray.join("',\n'"); |
97 | 3 | var intro = "if (typeof "+covVar+" === 'undefined') "+covVar+" = {};\n"; |
98 | 3 | intro += "if (typeof "+covVar+"['"+filename+"'] === 'undefined'){"; |
99 | | |
100 | 3 | intro += covVar+"['"+filename+"']=[];\n"; |
101 | 3 | intro += covVar+"['"+filename+"'].source=['"+sourceString+"'];\n"; |
102 | | //initialize array values |
103 | 3 | blanket._trackingArraySetup.sort(function(a,b){ |
104 | 9 | return parseInt(a,10) > parseInt(b,10); |
105 | | }).forEach(function(item){ |
106 | 10 | intro += covVar+"['"+filename+"']["+item+"]=0;\n"; |
107 | | }); |
108 | | |
109 | 3 | intro += "}"; |
110 | 3 | return intro; |
111 | | }, |
112 | | _blockifyIf: function(node){ |
113 | | |
114 | 87 | if (linesToAddBrackets.indexOf(node.type) > -1){ |
115 | 7 | var bracketsExistObject = node.consequent || node.body; |
116 | 7 | var bracketsExistAlt = node.alternate; |
117 | 7 | if( bracketsExistAlt && bracketsExistAlt.type !== "BlockStatement") { |
118 | 2 | bracketsExistAlt.update("{\n"+bracketsExistAlt.source()+"}\n"); |
119 | | } |
120 | 7 | if( bracketsExistObject && bracketsExistObject.type !== "BlockStatement") { |
121 | 6 | bracketsExistObject.update("{\n"+bracketsExistObject.source()+"}\n"); |
122 | | } |
123 | | } |
124 | | }, |
125 | | _addTracking: function (node,filename) { |
126 | 87 | blanket._blockifyIf(node); |
127 | 87 | if (linesToAddTracking.indexOf(node.type) > -1){ |
128 | 19 | if (node.type === "VariableDeclaration" && |
129 | | (node.parent.type === "ForStatement" || node.parent.type === "ForInStatement")){ |
130 | 0 | return; |
131 | | } |
132 | 19 | if (node.loc && node.loc.start){ |
133 | 19 | node.update(covVar+"['"+filename+"']["+node.loc.start.line+"]++;\n"+node.source()); |
134 | 19 | blanket._trackingArraySetup.push(node.loc.start.line); |
135 | | }else{ |
136 | | //I don't think we can handle a node with no location |
137 | 0 | throw new Error("The instrumenter encountered a node with no location: "+Object.keys(node)); |
138 | | } |
139 | | } |
140 | | }, |
141 | | setupCoverage: function(){ |
142 | 1 | coverageInfo.instrumentation = "blanket"; |
143 | 1 | coverageInfo.stats = { |
144 | | "suites": 0, |
145 | | "tests": 0, |
146 | | "passes": 0, |
147 | | "pending": 0, |
148 | | "failures": 0, |
149 | | "start": new Date() |
150 | | }; |
151 | | }, |
152 | | _checkIfSetup: function(){ |
153 | 4 | if (!coverageInfo.stats){ |
154 | 0 | throw new Error("You must call blanket.setupCoverage() first."); |
155 | | } |
156 | | }, |
157 | | testEvents: { |
158 | | onTestStart: function(){ |
159 | 1 | blanket._checkIfSetup(); |
160 | 1 | coverageInfo.stats.tests++; |
161 | 1 | coverageInfo.stats.pending++; |
162 | | }, |
163 | | onTestDone: function(total,passed){ |
164 | 1 | blanket._checkIfSetup(); |
165 | 1 | if(passed === total){ |
166 | 1 | coverageInfo.stats.passes++; |
167 | | }else{ |
168 | 0 | coverageInfo.stats.failures++; |
169 | | } |
170 | 1 | coverageInfo.stats.pending--; |
171 | | }, |
172 | | onModuleStart: function(){ |
173 | 1 | blanket._checkIfSetup(); |
174 | 1 | coverageInfo.stats.suites++; |
175 | | }, |
176 | | onTestsDone: function(){ |
177 | 1 | blanket._checkIfSetup(); |
178 | 1 | coverageInfo.stats.end = new Date(); |
179 | 1 | if (typeof exports === 'undefined'){ |
180 | 0 | blanket.report(coverageInfo); |
181 | | }else{ |
182 | 1 | blanket.getReporter().call(this,coverageInfo); |
183 | | } |
184 | | } |
185 | | } |
186 | | }; |
187 | 1 | return blanket; |
188 | | })(); |
189 | | |