Improve release QA dashboard diagnostics
This commit is contained in:
@@ -82,7 +82,13 @@ function writeReleaseDashboard({ qaVersion, commit, shortCommit, qaReport, qaRep
|
||||
const qa = readJsonFromDist(qaReport);
|
||||
const repeat = qaRepeatReport ? readJsonFromDist(qaRepeatReport) : undefined;
|
||||
const missedObjectives = repeat?.missedObjectives ?? qa?.missedObjectives ?? [];
|
||||
const battleOutcomes = repeat?.battleOutcomes ?? battleOutcomesFromQaReport(qa);
|
||||
const runRows = repeat?.runs ?? [];
|
||||
const processFailureCount = repeat?.summary?.runsWithProcessFailures ?? 0;
|
||||
const battleFailureCount = battleOutcomes
|
||||
.filter((outcome) => outcome.outcome !== 'victory')
|
||||
.reduce((total, outcome) => total + (outcome.count ?? 1), 0);
|
||||
const totalMissedObjectives = repeat?.summary?.totalMissedObjectives ?? qa?.summary?.missedObjectiveCount ?? missedObjectives.length;
|
||||
|
||||
const html = `<!doctype html>
|
||||
<html lang="ko">
|
||||
@@ -98,7 +104,7 @@ function writeReleaseDashboard({ qaVersion, commit, shortCommit, qaReport, qaRep
|
||||
h1 { font-size: 28px; }
|
||||
h2 { font-size: 18px; margin-top: 28px; }
|
||||
.meta, .grid, table { margin-top: 16px; }
|
||||
.meta { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
|
||||
.meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr)); gap: 10px; }
|
||||
.tile { border: 1px solid #665941; background: #221f18; border-radius: 8px; padding: 12px; }
|
||||
.label { color: #b9ad91; font-size: 12px; }
|
||||
.value { margin-top: 5px; font-size: 18px; font-weight: 700; word-break: break-word; }
|
||||
@@ -108,6 +114,7 @@ function writeReleaseDashboard({ qaVersion, commit, shortCommit, qaReport, qaRep
|
||||
tr:last-child td { border-bottom: 0; }
|
||||
.ok { color: #a9db8f; }
|
||||
.warn { color: #f3c96b; }
|
||||
.bad { color: #ff9b86; }
|
||||
a { color: #9cc6ff; }
|
||||
@media (max-width: 820px) { .meta { grid-template-columns: 1fr 1fr; } }
|
||||
</style>
|
||||
@@ -120,6 +127,9 @@ function writeReleaseDashboard({ qaVersion, commit, shortCommit, qaReport, qaRep
|
||||
${metricTile('Commit', shortCommit)}
|
||||
${metricTile('Early QA', `${qa?.summary?.victories ?? 0}/${qa?.battleCount ?? 0} victories`)}
|
||||
${metricTile('Repeat QA', repeat ? `${repeat.summary.runs} runs` : 'not run')}
|
||||
${metricTile('Battle Failures', battleFailureCount)}
|
||||
${metricTile('Process Failures', processFailureCount)}
|
||||
${metricTile('Objective Misses', totalMissedObjectives)}
|
||||
</section>
|
||||
|
||||
<h2>Artifacts</h2>
|
||||
@@ -136,6 +146,23 @@ function writeReleaseDashboard({ qaVersion, commit, shortCommit, qaReport, qaRep
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Repeated Battle Outcomes</h2>
|
||||
<table>
|
||||
<thead><tr><th>Battle</th><th>Id</th><th>Outcome</th><th>Count</th></tr></thead>
|
||||
<tbody>
|
||||
${
|
||||
battleOutcomes.length > 0
|
||||
? battleOutcomes
|
||||
.map((outcome) => {
|
||||
const cssClass = outcome.outcome === 'victory' ? 'ok' : 'bad';
|
||||
return `<tr><td>${escapeHtml(String(outcome.battleNo))}</td><td>${escapeHtml(outcome.battleId ?? '')}</td><td class="${cssClass}">${escapeHtml(outcome.outcome)}</td><td>${escapeHtml(String(outcome.count ?? 1))}</td></tr>`;
|
||||
})
|
||||
.join('')
|
||||
: '<tr><td colspan="4">No battle outcome data recorded.</td></tr>'
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Repeated Objective Misses</h2>
|
||||
<table>
|
||||
<thead><tr><th>Battle</th><th>Objective</th><th>Misses</th><th>Rate</th><th>Latest Reason</th></tr></thead>
|
||||
@@ -155,17 +182,20 @@ function writeReleaseDashboard({ qaVersion, commit, shortCommit, qaReport, qaRep
|
||||
|
||||
<h2>Repeat Runs</h2>
|
||||
<table>
|
||||
<thead><tr><th>Run</th><th>Victories</th><th>Missed Objectives</th><th>Battles With Misses</th></tr></thead>
|
||||
<thead><tr><th>Run</th><th>Status</th><th>Exit</th><th>Victories</th><th>Battle Failures</th><th>Missed Objectives</th><th>Battles With Misses</th><th>Report</th></tr></thead>
|
||||
<tbody>
|
||||
${
|
||||
runRows.length > 0
|
||||
? runRows
|
||||
.map(
|
||||
(run) =>
|
||||
`<tr><td>${escapeHtml(String(run.run))}</td><td>${escapeHtml(String(run.victories))}</td><td>${escapeHtml(String(run.missedObjectiveCount))}</td><td>${escapeHtml((run.battlesWithMissedObjectives ?? []).join(', ') || '-')}</td></tr>`
|
||||
(run) => {
|
||||
const reportLink = distAssetPath(run.path);
|
||||
const statusClass = run.exitCode === 0 && run.failures === 0 ? 'ok' : 'bad';
|
||||
return `<tr><td>${escapeHtml(String(run.run))}</td><td class="${statusClass}">${escapeHtml(run.outcome ?? 'unknown')}</td><td>${escapeHtml(String(run.exitCode ?? 0))}</td><td>${escapeHtml(String(run.victories))}</td><td>${escapeHtml(String(run.failures ?? 0))}</td><td>${escapeHtml(String(run.missedObjectiveCount))}</td><td>${escapeHtml((run.battlesWithMissedObjectives ?? []).join(', ') || '-')}</td><td>${reportLink ? `<a href="${escapeAttribute(reportLink)}">${escapeHtml(reportLink)}</a>` : '-'}</td></tr>`;
|
||||
}
|
||||
)
|
||||
.join('')
|
||||
: '<tr><td colspan="4">Repeat QA was not run for this release.</td></tr>'
|
||||
: '<tr><td colspan="8">Repeat QA was not run for this release.</td></tr>'
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -179,6 +209,15 @@ function writeReleaseDashboard({ qaVersion, commit, shortCommit, qaReport, qaRep
|
||||
console.log(`Wrote dist/${releaseDashboardFile}`);
|
||||
}
|
||||
|
||||
function battleOutcomesFromQaReport(report) {
|
||||
return (report?.results ?? []).map((result) => ({
|
||||
battleNo: result.no,
|
||||
battleId: result.id,
|
||||
outcome: result.outcome,
|
||||
count: 1
|
||||
}));
|
||||
}
|
||||
|
||||
function readJsonFromDist(fileName) {
|
||||
return JSON.parse(readFileSync(join('dist', fileName), 'utf8'));
|
||||
}
|
||||
@@ -191,6 +230,13 @@ function formatRate(value) {
|
||||
return typeof value === 'number' ? `${Math.round(value * 100)}%` : '-';
|
||||
}
|
||||
|
||||
function distAssetPath(filePath) {
|
||||
if (!filePath) {
|
||||
return '';
|
||||
}
|
||||
return String(filePath).replaceAll('\\', '/').replace(/^dist\//, '');
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value)
|
||||
.replaceAll('&', '&')
|
||||
|
||||
@@ -138,6 +138,9 @@ try {
|
||||
const dashboard = await readTextAsset(targetUrl, releaseManifest.releaseDashboard);
|
||||
assert(dashboard.includes('Heros Web Release QA'), `Expected deployed release dashboard title: ${dashboard.slice(0, 200)}`);
|
||||
assert(dashboard.includes(releaseManifest.shortCommit ?? releaseManifest.commit), `Expected deployed release dashboard commit: ${dashboard.slice(0, 400)}`);
|
||||
assert(dashboard.includes('Repeated Battle Outcomes'), `Expected deployed release dashboard battle outcomes section: ${dashboard.slice(0, 800)}`);
|
||||
assert(dashboard.includes('Process Failures'), `Expected deployed release dashboard process failure metric: ${dashboard.slice(0, 800)}`);
|
||||
assert(dashboard.includes('Repeat Runs'), `Expected deployed release dashboard repeat runs section: ${dashboard.slice(0, 800)}`);
|
||||
console.log(`Verified release dashboard ${releaseManifest.releaseDashboard}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user