OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * A simple application that renders coverage results in a page. It sorts data | |
7 * by file name, shows a summary table including coverage statistics per | |
8 * package, and implements the necessary behaviour to select a file and view | |
9 * line-by-line coverage information. | |
10 */ | |
11 | |
12 var files_; | |
13 var code_; | |
14 var summary_; | |
15 var selectedDiv_; | |
16 var file_percent_ = {}; | |
17 var package_lines_ = {}; | |
18 var package_covered_ = {}; | |
19 var package_rec_lines_ = {}; | |
20 var package_rec_covered_ = {}; | |
21 | |
22 // handle a click event anywhere on the screen | |
23 function clickListener(e) { | |
24 var elem = e.target; | |
25 // determine if a file or a close link was clicked on: | |
26 while (elem != null) { | |
27 if (elem.classList) { | |
28 if (elem.classList.contains("file")) { | |
29 fileSelected(elem); | |
30 return; | |
31 } else if (elem.classList.contains("close")) { | |
32 closeDetails(); | |
33 } | |
34 } | |
35 elem = elem.parentNode; | |
36 } | |
37 } | |
38 | |
39 // handle when a row in the list of files is selected | |
40 function fileSelected(elem) { | |
41 if (selectedDiv_ != null) { | |
42 selectedDiv_.classList.remove("file-selected"); | |
43 } | |
44 if (selectedDiv_ == elem) { | |
45 closeDetails(); | |
46 return; | |
47 } | |
48 selectedDiv_ = elem; | |
49 elem.classList.add("file-selected"); | |
50 var file = elem.getAttribute("data-field"); | |
51 var code_with_coverage = ""; | |
52 var lines = code_[file]; | |
53 var coverage = summary_[file]; | |
54 for (var lineNum = 0; lineNum < lines.length; lineNum++) { | |
55 var covered = coverage[lineNum] == 1; | |
56 var currentLine = lines[lineNum]. | |
57 replace(/</g, "<"). | |
58 replace(/>/g, ">"). | |
59 replace(/\n/g, "\\n"); | |
60 var iscode = coverage[lineNum] == 0 || coverage[lineNum] == 1; | |
61 code_with_coverage += | |
62 "<span class='linenum'><span class='" + | |
63 (covered && iscode ? "yes" : (iscode ? "no" : "")) + "'>" + | |
64 (lineNum + 1)+ "</span>" + | |
65 "<span>" + currentLine + "</span>" + | |
66 "</div>"; | |
67 } | |
68 | |
69 var detailsDiv = document.getElementById("details-body"); | |
70 detailsDiv.innerHTML = code_with_coverage; | |
71 var outerDiv = detailsDiv.parentNode; | |
72 outerDiv.classList.remove("hidden"); | |
73 } | |
74 | |
75 // closes the detail view that displays line-by-line coverage info | |
76 function closeDetails() { | |
77 if (selectedDiv_ != null) { | |
78 selectedDiv_.classList.remove("file-selected"); | |
79 selectedDiv_ = null; | |
80 } | |
81 var detailsDiv = document.getElementById("details-body"); | |
82 detailsDiv.parentNode.classList.add("hidden"); | |
83 } | |
84 | |
85 // generates an HTML string segment for a coverage-bar | |
86 function generatePercentBar(percent) { | |
87 return "<span class='coverage-bar'>" + | |
88 "<span class='coverage-bar-inner' style='width:" + percent + "px'>" + | |
89 "</span></span>"; | |
90 } | |
91 | |
92 // extracts the top-level directory from a path | |
93 function getRootDir(path) { | |
94 if (path == null) { | |
95 return null; | |
96 } | |
97 var index = path.indexOf("/"); | |
98 return index == -1 ? null : path.substring(0, index); | |
99 } | |
100 | |
101 // extracts the directory portion of a path | |
102 function getDirName(path) { | |
103 if (path == null) { | |
104 return null; | |
105 } | |
106 var index = path.lastIndexOf("/"); | |
107 return index == -1 ? null : path.substring(0, index); | |
108 } | |
109 | |
110 // extracts the file name portion of a path | |
111 function getFileName(path) { | |
112 if (path == null) { | |
113 return null; | |
114 } | |
115 var index = path.lastIndexOf("/"); | |
116 return index == -1 ? path : path.substring(index + 1); | |
117 } | |
118 | |
119 // generates an HTML string segment with the summary stats for a package which | |
120 // doesn't include sub-packages statistics. | |
121 function generatePackageLine(pkg) { | |
122 var percent = ((package_covered_[pkg] * 100) / package_lines_[pkg]). | |
123 toFixed(1); | |
124 return "<tr class='package'><td colspan='2' class='package'>" + | |
125 pkg + | |
126 "<td class='file-percent'>" + percent + "%</td>" + | |
127 "<td class='file-percent'>" + generatePercentBar(percent) + | |
128 "</td> </tr>"; | |
129 } | |
130 | |
131 // generates an HTML string segment with the summary stats for a package which | |
132 // includes sub-packages statistics | |
133 function generatePackageLineRec(pkg) { | |
134 var percent = ((package_rec_covered_[pkg] * 100) / | |
135 package_rec_lines_[pkg]).toFixed(1); | |
136 return "<tr class='package'><td colspan='2' class='package'>" + | |
137 pkg + " (with subpackages)" + | |
138 "<td class='file-percent'>" + percent + "%</td>" + | |
139 "<td class='file-percent'>" + generatePercentBar(percent) + | |
140 "</td> </tr>"; | |
141 } | |
142 | |
143 // generates an hTML string segment with the summary stats for a single file | |
144 function generateFileLine(file) { | |
145 var filename = getFileName(file); | |
146 var percent = file_percent_[file].toFixed(1) | |
147 return "<tr class='file' data-field='" + file + "'>" + | |
148 "<td></td>" + | |
149 "<td class='file-name'>" + | |
150 filename + | |
151 "</td>" + | |
152 "<td class='file-percent'>" + percent + "%</td>" + | |
153 "<td class='file-percent'>" + generatePercentBar(percent) + | |
154 "</td>" + | |
155 "</tr>"; | |
156 } | |
157 | |
158 // Updates coverage information in a package given the coverage data from a file | |
159 // that is directly on that package or on some sub-package | |
160 function recordPackageLinesRec(pkg, totalcode, covered) { | |
161 if (package_rec_lines_[pkg] == null) { | |
162 package_rec_lines_[pkg] = totalcode; | |
163 package_rec_covered_[pkg] = covered; | |
164 } else { | |
165 package_rec_lines_[pkg] += totalcode; | |
166 package_rec_covered_[pkg] += covered; | |
167 } | |
168 } | |
169 | |
170 // Updates coverage information in a package given the coverage data from a file | |
171 // directly on that package | |
172 function recordPackageLines(pkg, totalcode, covered) { | |
173 if (package_lines_[pkg] == null) { | |
174 package_lines_[pkg] = totalcode; | |
175 package_covered_[pkg] = covered; | |
176 } else { | |
177 package_lines_[pkg] += totalcode; | |
178 package_covered_[pkg] += covered; | |
179 } | |
180 } | |
181 | |
182 var EMPTY_ROW = "<tr><td> </td></tr>"; | |
183 | |
184 // renders the page with a list of files and an area to display more details. | |
185 function render(files, code, summary) { | |
186 files.sort() | |
187 files_ = files; | |
188 code_ = code; | |
189 summary_ = summary; | |
190 var buffer = ""; | |
191 var last_pkg = null; | |
192 | |
193 // compute percent for files and packages. Tally information per package, by | |
194 // tracking total lines covered on any file in the package | |
195 | |
196 for (var i = 0; i < files.length; i++) { | |
197 var file = files[i]; | |
198 var coverage = summary[file]; | |
199 var covered = 0; | |
200 var totalcode = 0; | |
201 for (var j = 0; j < coverage.length; j++) { | |
202 if (coverage[j] == 1 || coverage[j] == 0) { | |
203 totalcode += 1; | |
204 } | |
205 if (coverage[j] == 1) { | |
206 covered += 1; | |
207 } | |
208 } | |
209 file_percent_[file] = (covered * 100) / totalcode; | |
210 var pkg = getDirName(file); | |
211 | |
212 // summary for this package alone | |
213 recordPackageLines(pkg, totalcode, covered); | |
214 | |
215 // summary for each package including subpackages | |
216 while (pkg != null) { | |
217 recordPackageLinesRec(pkg, totalcode, covered); | |
218 pkg = getDirName(pkg) | |
219 } | |
220 recordPackageLinesRec("** everything **", totalcode, covered); | |
221 } | |
222 | |
223 // create UI for the results... | |
224 buffer += generatePackageLineRec("** everything **"); | |
225 for (var i = 0; i < files.length; i++) { | |
226 var file = files[i]; | |
227 | |
228 var pkg = getDirName(file) | |
229 if (pkg != last_pkg) { | |
230 var prefix = getRootDir(last_pkg); | |
231 var rec_summary = ""; | |
232 if (pkg.indexOf(prefix) != 0) { | |
233 var current = getDirName(pkg); | |
234 while (current != null) { | |
235 rec_summary = EMPTY_ROW + generatePackageLineRec(current) + | |
236 rec_summary; | |
237 current = getDirName(current); | |
238 } | |
239 } | |
240 buffer += rec_summary + EMPTY_ROW + generatePackageLineRec(pkg); | |
241 last_pkg = pkg; | |
242 } | |
243 buffer += generateFileLine(file); | |
244 } | |
245 | |
246 var menu = "<div class='menu'><table class='menu-table'><tbody>" + | |
247 buffer + "</tbody></table></div>" | |
248 | |
249 // single file details | |
250 var details = "<div class='details hidden'><div class='close'>X Close</div>" + | |
251 "<div id='details-body' class='details-body'></div></div>"; | |
252 | |
253 var div = document.createElement("div"); | |
254 div.innerHTML = "<div class='all'>" + | |
255 "<div>Select a file to display its details:</div> " | |
256 + menu + details + "</div>"; | |
257 document.body.appendChild(div); | |
258 document.body.addEventListener("click", clickListener, true); | |
259 } | |
OLD | NEW |