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  });