source: frontend/node_modules/istanbul-reports/lib/html/annotator.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/*
2 Copyright 2012-2015, Yahoo Inc.
3 Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4 */
5'use strict';
6
7const InsertionText = require('./insertion-text');
8const lt = '\u0001';
9const gt = '\u0002';
10const RE_LT = /</g;
11const RE_GT = />/g;
12const RE_AMP = /&/g;
13// eslint-disable-next-line
14var RE_lt = /\u0001/g;
15// eslint-disable-next-line
16var RE_gt = /\u0002/g;
17
18function title(str) {
19 return ' title="' + str + '" ';
20}
21
22function customEscape(text) {
23 text = String(text);
24 return text
25 .replace(RE_AMP, '&amp;')
26 .replace(RE_LT, '&lt;')
27 .replace(RE_GT, '&gt;')
28 .replace(RE_lt, '<')
29 .replace(RE_gt, '>');
30}
31
32function annotateLines(fileCoverage, structuredText) {
33 const lineStats = fileCoverage.getLineCoverage();
34 if (!lineStats) {
35 return;
36 }
37 Object.entries(lineStats).forEach(([lineNumber, count]) => {
38 if (structuredText[lineNumber]) {
39 structuredText[lineNumber].covered = count > 0 ? 'yes' : 'no';
40 structuredText[lineNumber].hits = count;
41 }
42 });
43}
44
45function annotateStatements(fileCoverage, structuredText) {
46 const statementStats = fileCoverage.s;
47 const statementMeta = fileCoverage.statementMap;
48 Object.entries(statementStats).forEach(([stName, count]) => {
49 const meta = statementMeta[stName];
50 const type = count > 0 ? 'yes' : 'no';
51 const startCol = meta.start.column;
52 let endCol = meta.end.column + 1;
53 const startLine = meta.start.line;
54 const endLine = meta.end.line;
55 const openSpan =
56 lt +
57 'span class="' +
58 (meta.skip ? 'cstat-skip' : 'cstat-no') +
59 '"' +
60 title('statement not covered') +
61 gt;
62 const closeSpan = lt + '/span' + gt;
63 let text;
64
65 if (type === 'no' && structuredText[startLine]) {
66 if (endLine !== startLine) {
67 endCol = structuredText[startLine].text.originalLength();
68 }
69 text = structuredText[startLine].text;
70 text.wrap(
71 startCol,
72 openSpan,
73 startCol < endCol ? endCol : text.originalLength(),
74 closeSpan
75 );
76 }
77 });
78}
79
80function annotateFunctions(fileCoverage, structuredText) {
81 const fnStats = fileCoverage.f;
82 const fnMeta = fileCoverage.fnMap;
83 if (!fnStats) {
84 return;
85 }
86 Object.entries(fnStats).forEach(([fName, count]) => {
87 const meta = fnMeta[fName];
88 const type = count > 0 ? 'yes' : 'no';
89 // Some versions of the instrumenter in the wild populate 'func'
90 // but not 'decl':
91 const decl = meta.decl || meta.loc;
92 const startCol = decl.start.column;
93 let endCol = decl.end.column + 1;
94 const startLine = decl.start.line;
95 const endLine = decl.end.line;
96 const openSpan =
97 lt +
98 'span class="' +
99 (meta.skip ? 'fstat-skip' : 'fstat-no') +
100 '"' +
101 title('function not covered') +
102 gt;
103 const closeSpan = lt + '/span' + gt;
104 let text;
105
106 if (type === 'no' && structuredText[startLine]) {
107 if (endLine !== startLine) {
108 endCol = structuredText[startLine].text.originalLength();
109 }
110 text = structuredText[startLine].text;
111 text.wrap(
112 startCol,
113 openSpan,
114 startCol < endCol ? endCol : text.originalLength(),
115 closeSpan
116 );
117 }
118 });
119}
120
121function annotateBranches(fileCoverage, structuredText) {
122 const branchStats = fileCoverage.b;
123 const branchMeta = fileCoverage.branchMap;
124 if (!branchStats) {
125 return;
126 }
127
128 Object.entries(branchStats).forEach(([branchName, branchArray]) => {
129 const sumCount = branchArray.reduce((p, n) => p + n, 0);
130 const metaArray = branchMeta[branchName].locations;
131 let i;
132 let count;
133 let meta;
134 let startCol;
135 let endCol;
136 let startLine;
137 let endLine;
138 let openSpan;
139 let closeSpan;
140 let text;
141
142 // only highlight if partial branches are missing or if there is a
143 // single uncovered branch.
144 if (sumCount > 0 || (sumCount === 0 && branchArray.length === 1)) {
145 // Need to recover the metaArray placeholder item to count an implicit else
146 if (
147 // Check if the branch is a conditional if branch.
148 branchMeta[branchName].type === 'if' &&
149 // Check if the branch has an implicit else.
150 branchArray.length === 2 &&
151 // Check if the implicit else branch is unaccounted for.
152 metaArray.length === 1 &&
153 // Check if the implicit else branch is uncovered.
154 branchArray[1] === 0
155 ) {
156 metaArray[1] = {
157 start: {},
158 end: {}
159 };
160 }
161
162 for (
163 i = 0;
164 i < branchArray.length && i < metaArray.length;
165 i += 1
166 ) {
167 count = branchArray[i];
168 meta = metaArray[i];
169 startCol = meta.start.column;
170 endCol = meta.end.column + 1;
171 startLine = meta.start.line;
172 endLine = meta.end.line;
173 openSpan =
174 lt +
175 'span class="branch-' +
176 i +
177 ' ' +
178 (meta.skip ? 'cbranch-skip' : 'cbranch-no') +
179 '"' +
180 title('branch not covered') +
181 gt;
182 closeSpan = lt + '/span' + gt;
183
184 // If the branch is an implicit else from an if statement,
185 // then the coverage report won't show a statistic.
186 // Therefore, the previous branch will be used to report that
187 // there is no coverage on that implicit branch.
188 if (
189 count === 0 &&
190 startLine === undefined &&
191 branchMeta[branchName].type === 'if'
192 ) {
193 const prevMeta = metaArray[i - 1];
194 startCol = prevMeta.start.column;
195 endCol = prevMeta.end.column + 1;
196 startLine = prevMeta.start.line;
197 endLine = prevMeta.end.line;
198 }
199
200 if (count === 0 && structuredText[startLine]) {
201 //skip branches taken
202 if (endLine !== startLine) {
203 endCol = structuredText[
204 startLine
205 ].text.originalLength();
206 }
207 text = structuredText[startLine].text;
208 if (branchMeta[branchName].type === 'if') {
209 // 'if' is a special case
210 // since the else branch might not be visible, being nonexistent
211 text.insertAt(
212 startCol,
213 lt +
214 'span class="' +
215 (meta.skip
216 ? 'skip-if-branch'
217 : 'missing-if-branch') +
218 '"' +
219 title(
220 (i === 0 ? 'if' : 'else') +
221 ' path not taken'
222 ) +
223 gt +
224 (i === 0 ? 'I' : 'E') +
225 lt +
226 '/span' +
227 gt,
228 true,
229 false
230 );
231 } else {
232 text.wrap(
233 startCol,
234 openSpan,
235 startCol < endCol ? endCol : text.originalLength(),
236 closeSpan
237 );
238 }
239 }
240 }
241 }
242 });
243}
244
245function annotateSourceCode(fileCoverage, sourceStore) {
246 let codeArray;
247 let lineCoverageArray;
248 try {
249 const sourceText = sourceStore.getSource(fileCoverage.path);
250 const code = sourceText.split(/(?:\r?\n)|\r/);
251 let count = 0;
252 const structured = code.map(str => {
253 count += 1;
254 return {
255 line: count,
256 covered: 'neutral',
257 hits: 0,
258 text: new InsertionText(str, true)
259 };
260 });
261 structured.unshift({
262 line: 0,
263 covered: null,
264 text: new InsertionText('')
265 });
266 annotateLines(fileCoverage, structured);
267 //note: order is important, since statements typically result in spanning the whole line and doing branches late
268 //causes mismatched tags
269 annotateBranches(fileCoverage, structured);
270 annotateFunctions(fileCoverage, structured);
271 annotateStatements(fileCoverage, structured);
272 structured.shift();
273
274 codeArray = structured.map(
275 item => customEscape(item.text.toString()) || '&nbsp;'
276 );
277
278 lineCoverageArray = structured.map(item => ({
279 covered: item.covered,
280 hits: item.hits > 0 ? item.hits + 'x' : '&nbsp;'
281 }));
282
283 return {
284 annotatedCode: codeArray,
285 lineCoverage: lineCoverageArray,
286 maxLines: structured.length
287 };
288 } catch (ex) {
289 codeArray = [ex.message];
290 lineCoverageArray = [{ covered: 'no', hits: 0 }];
291 String(ex.stack || '')
292 .split(/\r?\n/)
293 .forEach(line => {
294 codeArray.push(line);
295 lineCoverageArray.push({ covered: 'no', hits: 0 });
296 });
297 return {
298 annotatedCode: codeArray,
299 lineCoverage: lineCoverageArray,
300 maxLines: codeArray.length
301 };
302 }
303}
304
305module.exports = annotateSourceCode;
Note: See TracBrowser for help on using the repository browser.