...
1/*
2Copyright 2018 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17import { Coverage, parseCoverage } from "./parser";
18import { enumerate, map } from "./utils";
19
20declare const embeddedProfiles: Array<{ path: string; content: string }>;
21
22let coverageFiles: Array<{ name: string; coverage: Coverage }> = [];
23let gPrefix = "";
24
25function filenameForDisplay(path: string): string {
26 const basename = path.split("/").pop()!;
27 const withoutSuffix = basename.replace(/\.[^.]+$/, "");
28 return withoutSuffix;
29}
30
31function loadEmbeddedProfiles(): Array<{ name: string; coverage: Coverage }> {
32 return embeddedProfiles.map(({ path, content }) => ({
33 coverage: parseCoverage(content),
34 name: filenameForDisplay(path),
35 }));
36}
37
38async function loadProfile(path: string): Promise<Coverage> {
39 const response = await fetch(path, { credentials: "include" });
40 const content = await response.text();
41 return parseCoverage(content);
42}
43
44async function init(): Promise<void> {
45 if (location.hash.length > 1) {
46 gPrefix = location.hash.substring(1);
47 }
48
49 coverageFiles = loadEmbeddedProfiles();
50 google.charts.load("current", { packages: ["table"] });
51 google.charts.setOnLoadCallback(drawTable);
52}
53
54function updateBreadcrumb(): void {
55 const parts = gPrefix.split("/");
56 const parent = document.getElementById("breadcrumbs")!;
57 parent.innerHTML = "";
58 let prefixSoFar = "";
59 for (const part of parts) {
60 if (!part) {
61 continue;
62 }
63 prefixSoFar += part + "/";
64 const node = document.createElement("a");
65 node.href = `#${prefixSoFar}`;
66 node.innerText = part;
67 parent.appendChild(node);
68 parent.appendChild(document.createTextNode("/"));
69 }
70}
71
72function coveragesForPrefix(
73 coverages: Coverage[],
74 prefix: string
75): Iterable<{ c: Array<{ v: number | string; f?: string }> }> {
76 const m = mergeMaps(
77 map(coverages, (c) => c.getCoverageForPrefix(prefix).children)
78 );
79 const keys = Array.from(m.keys());
80 keys.sort();
81 console.log(m);
82 return map(keys, (k) => ({
83 c: [{ v: k } as { v: number | string; f?: string }].concat(
84 m.get(k)!.map((x, i) => {
85 if (!x) {
86 return { v: "" };
87 }
88 const next = m.get(k)![i + 1];
89 const coverage = x.coveredStatements / x.totalStatements;
90 let arrow = "";
91 if (next) {
92 const nextCoverage = next.coveredStatements / next.totalStatements;
93 if (coverage > nextCoverage) {
94 arrow = "▲";
95 } else if (coverage < nextCoverage) {
96 arrow = "▼";
97 }
98 }
99 const percentage = `${(coverage * 100).toFixed(1)}%`;
100 return {
101 f: `<span class="arrow">${arrow}</span> ${percentage}`,
102 v: coverage,
103 };
104 })
105 ),
106 }));
107}
108
109function mergeMaps<T, U>(maps: Iterable<Map<T, U>>): Map<T, U[]> {
110 const result = new Map();
111 for (const [i, m] of enumerate(maps)) {
112 for (const [key, value] of m.entries()) {
113 if (!result.has(key)) {
114 result.set(key, Array(i).fill(null));
115 }
116 result.get(key).push(value);
117 }
118 for (const entry of result.values()) {
119 if (entry.length === i) {
120 entry.push(null);
121 }
122 }
123 }
124 return result;
125}
126
127function drawTable(): void {
128 const rows = Array.from(
129 coveragesForPrefix(
130 coverageFiles.map((x) => x.coverage),
131 gPrefix
132 )
133 );
134 const cols = coverageFiles.map((x, i) => ({
135 id: `file-${i}`,
136 label: x.name,
137 type: "number",
138 }));
139 const dataTable = new google.visualization.DataTable({
140 cols: [{ id: "child", label: "File", type: "string" }].concat(cols),
141 rows,
142 });
143
144 const colourFormatter = new google.visualization.ColorFormat();
145 colourFormatter.addGradientRange(0, 1.0001, "#FFFFFF", "#DD0000", "#00DD00");
146 for (let i = 1; i < cols.length + 1; ++i) {
147 colourFormatter.format(dataTable, i);
148 }
149
150 const table = new google.visualization.Table(
151 document.getElementById("table")!
152 );
153 table.draw(dataTable, { allowHtml: true });
154
155 google.visualization.events.addListener(table, "select", () => {
156 const child = rows[table.getSelection()[0].row!].c[0].v as string;
157 if (child.endsWith("/")) {
158 location.hash = gPrefix + child;
159 } else {
160 // TODO: this shouldn't be hardcoded.
161 // location.href = 'profiles/everything-diff.html#file' +
162 // coverage.getFile(prefix + child).fileNumber;
163 }
164 });
165 updateBreadcrumb();
166}
167
168document.addEventListener("DOMContentLoaded", () => init());
169window.addEventListener("hashchange", () => {
170 gPrefix = location.hash.substring(1);
171 drawTable();
172});
View as plain text