Skip to content

Commit d484689

Browse files
committed
dashboard: Indicate if a test was rerun
Added code to get rerun information to both fetch scripts. Display the reruns as a superscript in the rowExpansionTemplate with the result/URL. Fixes: #8 Signed-off-by: Anna Finn
1 parent d2ba40b commit d484689

File tree

4 files changed

+352
-117
lines changed

4 files changed

+352
-117
lines changed

Diff for: components/weatherTemplate.js

+4-11
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,33 @@ const icons = [
1111

1212
export const getWeatherIndex = (stat) => {
1313
let fail_rate = 0;
14-
fail_rate = (stat["fails"] + stat["skips"]) / stat["runs"];
14+
fail_rate = (stat["fails"]) / stat["runs"];
1515
// e.g. failing 3/9 runs is .33, or idx=1
1616
var idx = Math.floor((fail_rate * 10) / 2);
1717
if (idx == icons.length) {
1818
// edge case: if 100% failures, then we go past the end of icons[]
1919
// back the idx down by 1
20-
console.assert(fail_rate == 1.0);
2120
idx -= 1;
2221
}
2322

2423
// This error checks if there are zero runs.
2524
// Currently, will display stormy weather.
26-
if (isNaN(idx)) {
25+
if (isNaN(idx) || idx > icons.length) {
2726
idx = 4;
2827
}
2928
return idx;
3029
};
3130

32-
const getWeatherIcon = (stat) => {
33-
const idx = getWeatherIndex(stat);
34-
return icons[idx];
35-
};
36-
3731
export const weatherTemplate = (data) => {
38-
const icon = getWeatherIcon(data);
3932
return (
4033
<div>
4134
<Image
42-
src={`${basePath}/${icon}`}
35+
src={`${basePath}/${icons[getWeatherIndex(data)]}`}
4336
alt="weather"
4437
width={32}
4538
height={32}
4639
// priority
4740
/>
4841
</div>
4942
);
50-
};
43+
};

Diff for: pages/index.js

+116-53
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { useEffect, useState } from "react";
1+
import React, { useEffect, useState, useRef } from "react";
22
import { DataTable } from "primereact/datatable";
33
import { Column } from "primereact/column";
44
import Head from "next/head";
55
import { weatherTemplate, getWeatherIndex } from "../components/weatherTemplate";
6+
import { OverlayPanel } from 'primereact/overlaypanel';
7+
68

79

810
export default function Home() {
@@ -62,6 +64,8 @@ useEffect(() => {
6264
skips : job.skips,
6365
required : job.required,
6466
weather : getWeatherIndex(job),
67+
reruns : job.reruns,
68+
total_reruns : job.reruns.reduce((total, r) => total + r, 0),
6569
}))
6670
);
6771
setLoading(false);
@@ -81,6 +85,8 @@ useEffect(() => {
8185
skips : check.skips,
8286
required : check.required,
8387
weather : getWeatherIndex(check),
88+
reruns : check.reruns,
89+
total_reruns : check.reruns.reduce((total, r) => total + r, 0),
8490
}))
8591
);
8692
setLoading(false);
@@ -94,76 +100,129 @@ useEffect(() => {
94100

95101

96102

97-
const toggleRow = (rowData) => {
98-
const isRowExpanded = expandedRows.includes(rowData);
99-
100-
let updatedExpandedRows;
101-
if (isRowExpanded) {
102-
updatedExpandedRows = expandedRows.filter((r) => r !== rowData);
103-
} else {
104-
updatedExpandedRows = [...expandedRows, rowData];
105-
}
106-
107-
setExpandedRows(updatedExpandedRows);
108-
};
109-
110103
const tabClass = (active) => `tab md:px-4 px-2 py-2 border-b-2 focus:outline-none
111104
${active ? "border-blue-500 bg-gray-300"
112105
: "border-gray-300 bg-white hover:bg-gray-100"}`;
113106

114107

115108
// Template for rendering the Name column as a clickable item
116-
const nameTemplate = (rowData) => {
117-
return (
118-
<span onClick={() => toggleRow(rowData)} style={{ cursor: "pointer" }}>
119-
{rowData.name}
120-
</span>
109+
const nameTemplate = (rowData) => (
110+
<div className="cursor-pointer" onClick={() => toggleRow(rowData)}>
111+
<span style={{ userSelect: 'text' }}>{rowData.name}</span>
112+
</div>
113+
);
114+
115+
const toggleRow = (rowData) => {
116+
setExpandedRows((prev) =>
117+
prev.includes(rowData)
118+
? prev.filter((r) => r !== rowData)
119+
: [...prev, rowData]
121120
);
122121
};
123122

123+
const rerunRefs = useRef([]);
124+
124125
const rowExpansionTemplate = (data) => {
125126
const job = (display === "nightly"
126127
? jobs
127128
: checks).find((job) => job.name === data.name);
128129

130+
if (!job) return (
131+
<div className="p-3 bg-gray-100">
132+
No data available for this job.
133+
</div>);
134+
135+
136+
const getRunStatusIcon = (runs) => {
137+
if (Array.isArray(runs)) {
138+
const allPass = runs.every(run => run === "Pass");
139+
const allFail = runs.every(run => run === "Fail");
140+
141+
if (allPass) {return "✅";}
142+
if (allFail) {return "❌";}
143+
} else if (runs === "Pass") {
144+
return "✅";
145+
} else if (runs === "Fail") {
146+
return "❌";
147+
}
148+
return "⚠️"; // return a warning if a mix of results
149+
};
129150

130-
// Prepare run data
131-
const runs = [];
132-
for (let i = 0; i < job.runs; i++) {
133-
runs.push({
134-
run_num: job.run_nums[i],
135-
result: job.results[i],
136-
url: job.urls[i],
137-
});
138-
}
151+
const runEntries = job.run_nums.map((run_num, idx) => ({
152+
run_num,
153+
result: job.results[idx],
154+
reruns: job.reruns[idx],
155+
rerun_result: job.rerun_results[idx],
156+
url: job.urls[idx],
157+
attempt_urls: job.attempt_urls[idx],
158+
}));
139159

140160
return (
141-
<div
142-
key={`${job.name}-runs`}
143-
className="p-3 bg-gray-100"
144-
style={{ marginLeft: "4.5rem", marginTop: "-2.0rem" }}
145-
>
146-
<div>
147-
{runs.length > 0 ? (
148-
runs.map((run) => {
149-
const emoji =
150-
run.result === "Pass"
151-
? "✅"
152-
: run.result === "Fail"
153-
? "❌"
154-
: "⚠️";
155-
return (
156-
<span key={`${job.name}-runs-${run.run_num}`}>
157-
<a href={run.url}>
158-
{emoji} {run.run_num}
161+
<div key={`${job.name}-runs`} className="p-3 bg-gray-100">
162+
<div className="flex flex-wrap gap-4">
163+
{runEntries.map(({
164+
run_num,
165+
result,
166+
url,
167+
reruns,
168+
rerun_result,
169+
attempt_urls
170+
}, idx) => {
171+
const allResults = rerun_result
172+
? [result, ...rerun_result]
173+
: [result];
174+
175+
const runStatuses = allResults.map((result, idx) =>
176+
`${allResults.length - idx}. ${result === 'Pass'
177+
? '✅ Success'
178+
: result === 'Fail'
179+
? '❌ Fail'
180+
: '⚠️ Warning'}`);
181+
182+
// IDs can't have a '/'...
183+
const sanitizedJobName = job.name.replace(/[^a-zA-Z0-9-_]/g, '');
184+
185+
const badgeReruns = `reruns-${sanitizedJobName}-${run_num}`;
186+
187+
rerunRefs.current[badgeReruns] = rerunRefs.current[badgeReruns]
188+
|| React.createRef();
189+
190+
return (
191+
<div key={run_num} className="flex">
192+
<div key={idx} className="flex items-center">
193+
{/* <a href={url} target="_blank" rel="noopener noreferrer"> */}
194+
<a href={attempt_urls[0]} target="_blank" rel="noopener noreferrer">
195+
{getRunStatusIcon(allResults)} {run_num}
159196
</a>
160-
&nbsp;&nbsp;&nbsp;&nbsp;
161-
</span>
162-
);
163-
})
164-
) : (
165-
<div>No Nightly Runs associated with this job</div>
166-
)}
197+
</div>
198+
{reruns > 0 &&(
199+
<span className="p-overlay-badge">
200+
<sup className="text-xs font-bold align-super ml-1"
201+
onMouseEnter={(e) =>
202+
rerunRefs.current[badgeReruns].current.toggle(e)}>
203+
{reruns+1}
204+
</sup>
205+
<OverlayPanel ref={rerunRefs.current[badgeReruns]} dismissable
206+
onMouseLeave={(e) =>
207+
rerunRefs.current[badgeReruns].current.toggle(e)}>
208+
<ul className="bg-white border rounded shadow-lg p-2">
209+
{runStatuses.map((status, index) => (
210+
<li key={index} className="p-2 hover:bg-gray-200">
211+
<a
212+
href={attempt_urls[index] || `${url}/attempts/${index}`}
213+
target="_blank"
214+
rel="noopener noreferrer">
215+
{status}
216+
</a>
217+
</li>
218+
))}
219+
</ul>
220+
</OverlayPanel>
221+
</span>
222+
)}
223+
</div>
224+
);
225+
})}
167226
</div>
168227
</div>
169228
);
@@ -179,6 +238,7 @@ useEffect(() => {
179238
onRowToggle={(e) => setExpandedRows(e.data)}
180239
loading={loading}
181240
emptyMessage="No results found."
241+
sortField="fails"
182242
>
183243
<Column expander/>
184244
<Column
@@ -195,6 +255,7 @@ useEffect(() => {
195255
header = "Runs"
196256
className="whitespace-nowrap px-2"
197257
sortable />
258+
<Column field = "total_reruns" header = "Reruns" sortable/>
198259
<Column field = "fails" header = "Fails" sortable/>
199260
<Column field = "skips" header = "Skips" sortable/>
200261
<Column
@@ -214,6 +275,7 @@ useEffect(() => {
214275
onRowToggle={(e) => setExpandedRows(e.data)}
215276
loading={loading}
216277
emptyMessage="No results found."
278+
sortField="fails"
217279
>
218280
<Column expander/>
219281
<Column
@@ -230,6 +292,7 @@ useEffect(() => {
230292
header = "Runs"
231293
className="whitespace-nowrap px-2"
232294
sortable />
295+
<Column field = "total_reruns" header = "Reruns" sortable/>
233296
<Column field = "fails" header = "Fails" sortable/>
234297
<Column field = "skips" header = "Skips" sortable/>
235298
<Column

0 commit comments

Comments
 (0)