github.com/GoogleCloudPlatform/testgrid@v0.0.174/web/src/testgrid-dashboard-summary.ts (about) 1 import { LitElement, html } from 'lit'; 2 // eslint-disable-next-line @typescript-eslint/no-unused-vars 3 import { customElement, property, state } from 'lit/decorators.js'; 4 import { map } from 'lit/directives/map.js'; 5 import { Timestamp } from './gen/google/protobuf/timestamp.js'; 6 import { FailuresSummary, ListTabSummariesResponse, TabSummary } from './gen/pb/api/v1/data.js'; 7 import './tab-summary.js'; 8 9 export interface TabSummaryInfo { 10 icon: string; 11 name: string; 12 overallStatus: string; 13 detailedStatusMsg: string; 14 lastUpdateTimestamp: string; 15 lastRunTimestamp: string; 16 latestGreenBuild: string; 17 dashboardName: string; 18 failuresSummary?: FailuresSummaryInfo; 19 healthinessSummary?: HealthinessSummaryInfo; 20 } 21 22 export interface FailuresSummaryInfo { 23 topFailingTests: FailingTestInfo[]; 24 failureStats: FailureStats; 25 } 26 27 export interface FailingTestInfo { 28 displayName: string; 29 failCount: number; 30 passTimestamp: string; 31 failTimestamp: string; 32 } 33 34 export interface FailureStats { 35 numFailingTests: number; 36 } 37 38 export interface HealthinessSummaryInfo { 39 topFlakyTests: FlakyTestInfo[]; 40 healthinessStats: HealthinessStats; 41 } 42 43 export interface FlakyTestInfo { 44 displayName: string; 45 flakiness: number; 46 } 47 48 export interface HealthinessStats { 49 startTimestamp: string; 50 endTimestamp: string; 51 numFlakyTests: number; 52 averageFlakiness: number; 53 previousFlakiness: number; 54 } 55 56 // TODO: define in a shared file (dashboard group also uses this) 57 export const TabStatusIcon = new Map<string, string>([ 58 ['PASSING', 'done'], 59 ['FAILING', 'warning'], 60 ['FLAKY', 'remove_circle_outline'], 61 ['STALE', 'error_outline'], 62 ['BROKEN', 'broken_image'], 63 ['PENDING', 'schedule'], 64 ['ACCEPTABLE', 'add_circle_outline'], 65 ]); 66 67 // TODO: generate the correct time representation 68 function convertResponse(ts: TabSummary) { 69 const tsi: TabSummaryInfo = { 70 icon: TabStatusIcon.get(ts.overallStatus)!, 71 name: ts.tabName, 72 overallStatus: ts.overallStatus, 73 detailedStatusMsg: ts.detailedStatusMessage, 74 lastUpdateTimestamp: Timestamp.toDate( 75 ts.lastUpdateTimestamp! 76 ).toISOString(), 77 lastRunTimestamp: Timestamp.toDate(ts.lastRunTimestamp!).toISOString(), 78 latestGreenBuild: ts.latestPassingBuild, 79 dashboardName: ts.dashboardName, 80 }; 81 if (ts.failuresSummary !== undefined) { 82 tsi.failuresSummary = {} as FailuresSummaryInfo 83 const failureStats: FailureStats = { 84 numFailingTests: ts.failuresSummary!.failureStats!.numFailingTests, 85 } 86 tsi.failuresSummary!.failureStats = failureStats 87 88 tsi.failuresSummary!.topFailingTests = []; 89 ts.failuresSummary?.topFailingTests.forEach( (test, i) => { 90 const failingTest: FailingTestInfo = { 91 displayName: test.displayName, 92 failCount: test.failCount, 93 passTimestamp: Timestamp.toDate(test.passTimestamp!).toISOString(), 94 failTimestamp: Timestamp.toDate(test.failTimestamp!).toISOString(), 95 } 96 tsi.failuresSummary!.topFailingTests.push(failingTest) 97 }); 98 } 99 100 if (ts.healthinessSummary !== undefined) { 101 tsi.healthinessSummary = {} as HealthinessSummaryInfo 102 const healthinessStats: HealthinessStats = { 103 startTimestamp: Timestamp.toDate(ts.healthinessSummary!.healthinessStats!.start!).toISOString(), 104 endTimestamp: Timestamp.toDate(ts.healthinessSummary!.healthinessStats!.end!).toISOString(), 105 numFlakyTests: ts.healthinessSummary!.healthinessStats!.numFlakyTests, 106 averageFlakiness: ts.healthinessSummary!.healthinessStats!.averageFlakiness, 107 previousFlakiness: ts.healthinessSummary!.healthinessStats!.previousFlakiness, 108 } 109 tsi.healthinessSummary!.healthinessStats = healthinessStats 110 111 tsi.healthinessSummary!.topFlakyTests = []; 112 ts.healthinessSummary?.topFlakyTests.forEach( test => { 113 const flakyTest: FlakyTestInfo = { 114 displayName: test.displayName, 115 flakiness: test.flakiness, 116 } 117 tsi.healthinessSummary!.topFlakyTests.push(flakyTest) 118 }) 119 } 120 return tsi; 121 } 122 123 /** 124 * Class definition for the `testgrid-dashboard-summary` element. 125 * Renders the dashboard summary (summary of dashboard tabs). 126 */ 127 @customElement('testgrid-dashboard-summary') 128 // eslint-disable-next-line @typescript-eslint/no-unused-vars 129 export class TestgridDashboardSummary extends LitElement { 130 131 @property() 132 dashboardName: string = ''; 133 134 @state() 135 tabSummariesInfo: Array<TabSummaryInfo> = []; 136 137 connectedCallback(){ 138 super.connectedCallback(); 139 this.fetchTabSummaries(); 140 } 141 142 /** 143 * Lit-element lifecycle method. 144 * Invoked on each update to perform rendering tasks. 145 */ 146 render() { 147 return html` 148 ${map( 149 this.tabSummariesInfo, 150 (ts: TabSummaryInfo) => html`<tab-summary .info=${ts}></tab-summary>` 151 )} 152 `; 153 } 154 155 // fetch the tab summaries and tab names to populate the tab bar 156 private async fetchTabSummaries() { 157 try { 158 const response = await fetch( 159 `http://${process.env.API_HOST}:${process.env.API_PORT}/api/v1/dashboards/${this.dashboardName}/tab-summaries` 160 ); 161 if (!response.ok) { 162 throw new Error(`HTTP error: ${response.status}`); 163 } 164 const data = ListTabSummariesResponse.fromJson(await response.json()); 165 var tabSummaries: Array<TabSummaryInfo> = []; 166 data.tabSummaries.forEach(ts => { 167 const si = convertResponse(ts); 168 tabSummaries.push(si); 169 }); 170 this.tabSummariesInfo = tabSummaries; 171 } catch (error) { 172 console.error(`Could not get dashboard summaries: ${error}`); 173 } 174 } 175 }