source: trip-planner-front/node_modules/istanbul-lib-coverage/lib/file-coverage.js@ ceaed42

Last change on this file since ceaed42 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 7.6 KB
RevLine 
[6a3a178]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 percent = require('./percent');
8const dataProperties = require('./data-properties');
9const { CoverageSummary } = require('./coverage-summary');
10
11// returns a data object that represents empty coverage
12function emptyCoverage(filePath) {
13 return {
14 path: filePath,
15 statementMap: {},
16 fnMap: {},
17 branchMap: {},
18 s: {},
19 f: {},
20 b: {}
21 };
22}
23
24// asserts that a data object "looks like" a coverage object
25function assertValidObject(obj) {
26 const valid =
27 obj &&
28 obj.path &&
29 obj.statementMap &&
30 obj.fnMap &&
31 obj.branchMap &&
32 obj.s &&
33 obj.f &&
34 obj.b;
35 if (!valid) {
36 throw new Error(
37 'Invalid file coverage object, missing keys, found:' +
38 Object.keys(obj).join(',')
39 );
40 }
41}
42
43/**
44 * provides a read-only view of coverage for a single file.
45 * The deep structure of this object is documented elsewhere. It has the following
46 * properties:
47 *
48 * * `path` - the file path for which coverage is being tracked
49 * * `statementMap` - map of statement locations keyed by statement index
50 * * `fnMap` - map of function metadata keyed by function index
51 * * `branchMap` - map of branch metadata keyed by branch index
52 * * `s` - hit counts for statements
53 * * `f` - hit count for functions
54 * * `b` - hit count for branches
55 */
56class FileCoverage {
57 /**
58 * @constructor
59 * @param {Object|FileCoverage|String} pathOrObj is a string that initializes
60 * and empty coverage object with the specified file path or a data object that
61 * has all the required properties for a file coverage object.
62 */
63 constructor(pathOrObj) {
64 if (!pathOrObj) {
65 throw new Error(
66 'Coverage must be initialized with a path or an object'
67 );
68 }
69 if (typeof pathOrObj === 'string') {
70 this.data = emptyCoverage(pathOrObj);
71 } else if (pathOrObj instanceof FileCoverage) {
72 this.data = pathOrObj.data;
73 } else if (typeof pathOrObj === 'object') {
74 this.data = pathOrObj;
75 } else {
76 throw new Error('Invalid argument to coverage constructor');
77 }
78 assertValidObject(this.data);
79 }
80
81 /**
82 * returns computed line coverage from statement coverage.
83 * This is a map of hits keyed by line number in the source.
84 */
85 getLineCoverage() {
86 const statementMap = this.data.statementMap;
87 const statements = this.data.s;
88 const lineMap = Object.create(null);
89
90 Object.entries(statements).forEach(([st, count]) => {
91 /* istanbul ignore if: is this even possible? */
92 if (!statementMap[st]) {
93 return;
94 }
95 const { line } = statementMap[st].start;
96 const prevVal = lineMap[line];
97 if (prevVal === undefined || prevVal < count) {
98 lineMap[line] = count;
99 }
100 });
101 return lineMap;
102 }
103
104 /**
105 * returns an array of uncovered line numbers.
106 * @returns {Array} an array of line numbers for which no hits have been
107 * collected.
108 */
109 getUncoveredLines() {
110 const lc = this.getLineCoverage();
111 const ret = [];
112 Object.entries(lc).forEach(([l, hits]) => {
113 if (hits === 0) {
114 ret.push(l);
115 }
116 });
117 return ret;
118 }
119
120 /**
121 * returns a map of branch coverage by source line number.
122 * @returns {Object} an object keyed by line number. Each object
123 * has a `covered`, `total` and `coverage` (percentage) property.
124 */
125 getBranchCoverageByLine() {
126 const branchMap = this.branchMap;
127 const branches = this.b;
128 const ret = {};
129 Object.entries(branchMap).forEach(([k, map]) => {
130 const line = map.line || map.loc.start.line;
131 const branchData = branches[k];
132 ret[line] = ret[line] || [];
133 ret[line].push(...branchData);
134 });
135 Object.entries(ret).forEach(([k, dataArray]) => {
136 const covered = dataArray.filter(item => item > 0);
137 const coverage = (covered.length / dataArray.length) * 100;
138 ret[k] = {
139 covered: covered.length,
140 total: dataArray.length,
141 coverage
142 };
143 });
144 return ret;
145 }
146
147 /**
148 * return a JSON-serializable POJO for this file coverage object
149 */
150 toJSON() {
151 return this.data;
152 }
153
154 /**
155 * merges a second coverage object into this one, updating hit counts
156 * @param {FileCoverage} other - the coverage object to be merged into this one.
157 * Note that the other object should have the same structure as this one (same file).
158 */
159 merge(other) {
160 if (other.all === true) {
161 return;
162 }
163
164 if (this.all === true) {
165 this.data = other.data;
166 return;
167 }
168
169 Object.entries(other.s).forEach(([k, v]) => {
170 this.data.s[k] += v;
171 });
172 Object.entries(other.f).forEach(([k, v]) => {
173 this.data.f[k] += v;
174 });
175 Object.entries(other.b).forEach(([k, v]) => {
176 let i;
177 const retArray = this.data.b[k];
178 /* istanbul ignore if: is this even possible? */
179 if (!retArray) {
180 this.data.b[k] = v;
181 return;
182 }
183 for (i = 0; i < retArray.length; i += 1) {
184 retArray[i] += v[i];
185 }
186 });
187 }
188
189 computeSimpleTotals(property) {
190 let stats = this[property];
191
192 if (typeof stats === 'function') {
193 stats = stats.call(this);
194 }
195
196 const ret = {
197 total: Object.keys(stats).length,
198 covered: Object.values(stats).filter(v => !!v).length,
199 skipped: 0
200 };
201 ret.pct = percent(ret.covered, ret.total);
202 return ret;
203 }
204
205 computeBranchTotals() {
206 const stats = this.b;
207 const ret = { total: 0, covered: 0, skipped: 0 };
208
209 Object.values(stats).forEach(branches => {
210 ret.covered += branches.filter(hits => hits > 0).length;
211 ret.total += branches.length;
212 });
213 ret.pct = percent(ret.covered, ret.total);
214 return ret;
215 }
216
217 /**
218 * resets hit counts for all statements, functions and branches
219 * in this coverage object resulting in zero coverage.
220 */
221 resetHits() {
222 const statements = this.s;
223 const functions = this.f;
224 const branches = this.b;
225 Object.keys(statements).forEach(s => {
226 statements[s] = 0;
227 });
228 Object.keys(functions).forEach(f => {
229 functions[f] = 0;
230 });
231 Object.keys(branches).forEach(b => {
232 branches[b].fill(0);
233 });
234 }
235
236 /**
237 * returns a CoverageSummary for this file coverage object
238 * @returns {CoverageSummary}
239 */
240 toSummary() {
241 const ret = {};
242 ret.lines = this.computeSimpleTotals('getLineCoverage');
243 ret.functions = this.computeSimpleTotals('f', 'fnMap');
244 ret.statements = this.computeSimpleTotals('s', 'statementMap');
245 ret.branches = this.computeBranchTotals();
246 return new CoverageSummary(ret);
247 }
248}
249
250// expose coverage data attributes
251dataProperties(FileCoverage, [
252 'path',
253 'statementMap',
254 'fnMap',
255 'branchMap',
256 's',
257 'f',
258 'b',
259 'all'
260]);
261
262module.exports = {
263 FileCoverage
264};
Note: See TracBrowser for help on using the repository browser.