UNPKG

7.33 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 ? -1 : astr > bstr ? 1: /* istanbul ignore next */ 0;
121 });
122 }
123 };
124 tree.visit(visitor);
125 return tree;
126}
127
128function findCommonParent(paths) {
129 if (paths.length === 0) {
130 return new Path([]);
131 }
132 var common = paths[0],
133 i;
134
135 for (i = 1; i < paths.length; i += 1) {
136 common = common.commonPrefixPath(paths[i]);
137 if (common.length === 0) {
138 break;
139 }
140 }
141 return common;
142}
143
144function toInitialList(coverageMap) {
145 var ret = [],
146 commonParent;
147 coverageMap.files().forEach(function (filePath) {
148 var p = new Path(filePath),
149 coverage = coverageMap.fileCoverageFor(filePath);
150 ret.push({
151 filePath: filePath,
152 path: p,
153 fileCoverage: coverage
154 });
155 });
156 commonParent = findCommonParent(ret.map(function (o) { return o.path.parent(); }));
157 if (commonParent.length > 0) {
158 ret.forEach(function (o) {
159 o.path.splice(0, commonParent.length);
160 });
161 }
162 return {
163 list: ret,
164 commonParent: commonParent
165 };
166}
167
168function toDirParents(list) {
169 var nodeMap = Object.create(null),
170 parentNodeList = [];
171 list.forEach(function (o) {
172 var node = new ReportNode(o.path, o.fileCoverage),
173 parentPath = o.path.parent(),
174 parent = nodeMap[parentPath.toString()];
175
176 if (!parent) {
177 parent = new ReportNode(parentPath);
178 nodeMap[parentPath.toString()] = parent;
179 parentNodeList.push(parent);
180 }
181 parent.addChild(node);
182 });
183 return parentNodeList;
184}
185
186function foldIntoParents(nodeList) {
187 var ret = [], i, j;
188
189 // sort by longest length first
190 nodeList.sort(function (a, b) {
191 return -1 * Path.compare(a.path , b.path);
192 });
193
194 for (i = 0; i < nodeList.length; i += 1) {
195 var first = nodeList[i],
196 inserted = false;
197
198 for (j = i + 1; j < nodeList.length; j += 1) {
199 var second = nodeList[j];
200 if (second.path.ancestorOf(first.path)) {
201 second.addChild(first);
202 inserted = true;
203 break;
204 }
205 }
206
207 if (!inserted) {
208 ret.push(first);
209 }
210 }
211 return ret;
212}
213
214function createRoot() {
215 return new ReportNode(new Path([]));
216}
217
218function createNestedSummary(coverageMap) {
219 var flattened = toInitialList(coverageMap),
220 dirParents = toDirParents(flattened.list),
221 topNodes = foldIntoParents(dirParents),
222 root;
223
224 if (topNodes.length === 0) {
225 return treeFor(new ReportNode([]));
226 }
227
228 if (topNodes.length === 1) {
229 return treeFor(topNodes[0]);
230 }
231
232 root = createRoot();
233 topNodes.forEach(function (node) {
234 root.addChild(node);
235 });
236 return treeFor(root);
237}
238
239function createPackageSummary(coverageMap) {
240 var flattened = toInitialList(coverageMap),
241 dirParents = toDirParents(flattened.list),
242 common = flattened.commonParent,
243 prefix,
244 root;
245
246 if (dirParents.length === 1) {
247 root = dirParents[0];
248 } else {
249 root = createRoot();
250 // if one of the dirs is itself the root,
251 // then we need to create a top-level dir
252 dirParents.forEach(function (dp) {
253 if (dp.path.length === 0) {
254 prefix = 'root';
255 }
256 });
257 if (prefix && common.length > 0) {
258 prefix = common.elements()[common.elements().length - 1];
259 }
260 dirParents.forEach(function (node) {
261 root.addChild(node);
262 });
263 }
264 return treeFor(root, prefix);
265}
266
267function createFlatSummary(coverageMap) {
268 var flattened = toInitialList(coverageMap),
269 list = flattened.list,
270 root;
271
272 root = createRoot();
273 list.forEach(function (o) {
274 var node = new ReportNode(o.path, o.fileCoverage);
275 root.addChild(node);
276 });
277 return treeFor(root);
278}
279
280module.exports = {
281 createNestedSummary: createNestedSummary,
282 createPackageSummary: createPackageSummary,
283 createFlatSummary: createFlatSummary
284};