| 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 |