UNPKG

7.45 kBJavaScriptView Raw
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
7var Path = require('./path'),
8 util = require('util'),
9 tree = require('./tree'),
10 coverage = require('istanbul-lib-coverage'),
11 BaseNode = tree.Node,
12 BaseTree = tree.Tree;
13
14function ReportNode(path, fileCoverage) {
15 this.path = path;
16 this.parent = null;
17 this.fileCoverage = fileCoverage;
18 this.children = [];
19}
20
21util.inherits(ReportNode, BaseNode);
22
23ReportNode.prototype.addChild = function(child) {
24 child.parent = this;
25 this.children.push(child);
26};
27
28ReportNode.prototype.asRelative = function(p) {
29 /* istanbul ignore if */
30 if (p.substring(0, 1) === '/') {
31 return p.substring(1);
32 }
33 return p;
34};
35
36ReportNode.prototype.getQualifiedName = function() {
37 return this.asRelative(this.path.toString());
38};
39
40ReportNode.prototype.getRelativeName = function() {
41 var parent = this.getParent(),
42 myPath = this.path,
43 relPath,
44 i,
45 parentPath = parent ? parent.path : new Path([]);
46 if (parentPath.ancestorOf(myPath)) {
47 relPath = new Path(myPath.elements());
48 for (i = 0; i < parentPath.length; i += 1) {
49 relPath.shift();
50 }
51 return this.asRelative(relPath.toString());
52 }
53 return this.asRelative(this.path.toString());
54};
55
56ReportNode.prototype.getParent = function() {
57 return this.parent;
58};
59
60ReportNode.prototype.getChildren = function() {
61 return this.children;
62};
63
64ReportNode.prototype.isSummary = function() {
65 return !this.fileCoverage;
66};
67
68ReportNode.prototype.getFileCoverage = function() {
69 return this.fileCoverage;
70};
71
72ReportNode.prototype.getCoverageSummary = function(filesOnly) {
73 var cacheProp = 'c_' + (filesOnly ? 'files' : 'full'),
74 summary;
75
76 if (this.hasOwnProperty(cacheProp)) {
77 return this[cacheProp];
78 }
79
80 if (!this.isSummary()) {
81 summary = this.getFileCoverage().toSummary();
82 } else {
83 var count = 0;
84 summary = coverage.createCoverageSummary();
85 this.getChildren().forEach(function(child) {
86 if (filesOnly && child.isSummary()) {
87 return;
88 }
89 count += 1;
90 summary.merge(child.getCoverageSummary(filesOnly));
91 });
92 if (count === 0 && filesOnly) {
93 summary = null;
94 }
95 }
96 this[cacheProp] = summary;
97 return summary;
98};
99
100function treeFor(root, childPrefix) {
101 var tree = new BaseTree(),
102 visitor,
103 maybePrefix = function(node) {
104 if (childPrefix && !node.isRoot()) {
105 node.path.unshift(childPrefix);
106 }
107 };
108 tree.getRoot = function() {
109 return root;
110 };
111 visitor = {
112 onDetail: function(node) {
113 maybePrefix(node);
114 },
115 onSummary: function(node) {
116 maybePrefix(node);
117 node.children.sort(function(a, b) {
118 var astr = a.path.toString(),
119 bstr = b.path.toString();
120 return astr < bstr
121 ? -1
122 : astr > bstr
123 ? 1
124 : /* istanbul ignore next */ 0;
125 });
126 }
127 };
128 tree.visit(visitor);
129 return tree;
130}
131
132function findCommonParent(paths) {
133 if (paths.length === 0) {
134 return new Path([]);
135 }
136 var common = paths[0],
137 i;
138
139 for (i = 1; i < paths.length; i += 1) {
140 common = common.commonPrefixPath(paths[i]);
141 if (common.length === 0) {
142 break;
143 }
144 }
145 return common;
146}
147
148function toInitialList(coverageMap) {
149 var ret = [],
150 commonParent;
151 coverageMap.files().forEach(function(filePath) {
152 var p = new Path(filePath),
153 coverage = coverageMap.fileCoverageFor(filePath);
154 ret.push({
155 filePath: filePath,
156 path: p,
157 fileCoverage: coverage
158 });
159 });
160 commonParent = findCommonParent(
161 ret.map(function(o) {
162 return o.path.parent();
163 })
164 );
165 if (commonParent.length > 0) {
166 ret.forEach(function(o) {
167 o.path.splice(0, commonParent.length);
168 });
169 }
170 return {
171 list: ret,
172 commonParent: commonParent
173 };
174}
175
176function toDirParents(list) {
177 var nodeMap = Object.create(null),
178 parentNodeList = [];
179 list.forEach(function(o) {
180 var node = new ReportNode(o.path, o.fileCoverage),
181 parentPath = o.path.parent(),
182 parent = nodeMap[parentPath.toString()];
183
184 if (!parent) {
185 parent = new ReportNode(parentPath);
186 nodeMap[parentPath.toString()] = parent;
187 parentNodeList.push(parent);
188 }
189 parent.addChild(node);
190 });
191 return parentNodeList;
192}
193
194function foldIntoParents(nodeList) {
195 var ret = [],
196 i,
197 j;
198
199 // sort by longest length first
200 nodeList.sort(function(a, b) {
201 return -1 * Path.compare(a.path, b.path);
202 });
203
204 for (i = 0; i < nodeList.length; i += 1) {
205 var first = nodeList[i],
206 inserted = false;
207
208 for (j = i + 1; j < nodeList.length; j += 1) {
209 var second = nodeList[j];
210 if (second.path.ancestorOf(first.path)) {
211 second.addChild(first);
212 inserted = true;
213 break;
214 }
215 }
216
217 if (!inserted) {
218 ret.push(first);
219 }
220 }
221 return ret;
222}
223
224function createRoot() {
225 return new ReportNode(new Path([]));
226}
227
228function createNestedSummary(coverageMap) {
229 var flattened = toInitialList(coverageMap),
230 dirParents = toDirParents(flattened.list),
231 topNodes = foldIntoParents(dirParents),
232 root;
233
234 if (topNodes.length === 0) {
235 return treeFor(new ReportNode(new Path([])));
236 }
237
238 if (topNodes.length === 1) {
239 return treeFor(topNodes[0]);
240 }
241
242 root = createRoot();
243 topNodes.forEach(function(node) {
244 root.addChild(node);
245 });
246 return treeFor(root);
247}
248
249function createPackageSummary(coverageMap) {
250 var flattened = toInitialList(coverageMap),
251 dirParents = toDirParents(flattened.list),
252 common = flattened.commonParent,
253 prefix,
254 root;
255
256 if (dirParents.length === 1) {
257 root = dirParents[0];
258 } else {
259 root = createRoot();
260 // if one of the dirs is itself the root,
261 // then we need to create a top-level dir
262 dirParents.forEach(function(dp) {
263 if (dp.path.length === 0) {
264 prefix = 'root';
265 }
266 });
267 if (prefix && common.length > 0) {
268 prefix = common.elements()[common.elements().length - 1];
269 }
270 dirParents.forEach(function(node) {
271 root.addChild(node);
272 });
273 }
274 return treeFor(root, prefix);
275}
276
277function createFlatSummary(coverageMap) {
278 var flattened = toInitialList(coverageMap),
279 list = flattened.list,
280 root;
281
282 root = createRoot();
283 list.forEach(function(o) {
284 var node = new ReportNode(o.path, o.fileCoverage);
285 root.addChild(node);
286 });
287 return treeFor(root);
288}
289
290module.exports = {
291 createNestedSummary: createNestedSummary,
292 createPackageSummary: createPackageSummary,
293 createFlatSummary: createFlatSummary
294};