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