feat: show update download telemetry

Calculate download throughput and remaining time from verified installer bytes and display downloaded size, total size, speed, and ETA beneath the progress track while retaining the last values after cancellation.
This commit is contained in:
Sucukdeluxe
2026-08-21 00:28:48 +02:00
parent e7d30edab5
commit 4d3a4f1a8a
7 changed files with 67 additions and 7 deletions
+1
View File
@@ -176,4 +176,5 @@ test('header occupies its final geometry before asynchronous initialization', ()
assert.match(css, /\.header-update-button\.update-available:hover\s*\{[^}]*background:\s*var\(--success-end\);[^}]*color:\s*#000;/su);
assert.match(css, /\.update-dialog\s*\{[^}]*width:\s*min\(576px,\s*100%\);/su);
assert.match(css, /\.update-release-notes\s*\{[^}]*height:\s*min\(220px,\s*42vh\);/su);
assert.match(html, /class="update-progress-footer"[\s\S]*id="updateProgressDetails"[\s\S]*id="updateProgressText"/u);
});
+3 -1
View File
@@ -2460,7 +2460,7 @@ setTimeout(async () => {
const busyUpdateState = await wc.executeJavaScript(\`(() => {
showUpdateBanner({ remoteVersion: '9.9.9' });
handleUpdateProgress({ stage: 'downloading', percent: 50 });
handleUpdateProgress({ stage: 'downloading', percent: 50, bytesDownloaded: 40 * 1024 * 1024, bytesTotal: 100 * 1024 * 1024, bytesPerSecond: 8 * 1024 * 1024, etaSeconds: 8 });
const overlay = document.getElementById('updateBanner');
document.getElementById('updateCloseBtn').click();
document.getElementById('dismissUpdateBtn').click();
@@ -2480,6 +2480,7 @@ setTimeout(async () => {
messageText: document.getElementById('updateMessage').textContent,
progressLabel: progress.getAttribute('aria-label'),
progressText: progress.getAttribute('aria-valuetext'),
progressDetails: document.getElementById('updateProgressDetails').textContent,
progressState: progress.dataset.state,
progressColor: getComputedStyle(progress).backgroundColor,
progressTrackWidth: progress.parentElement.getBoundingClientRect().width,
@@ -2489,6 +2490,7 @@ setTimeout(async () => {
})()\`);
check('Busy update keeps its progress dialog open with a dangerous download cancellation action', busyUpdateState.display === 'flex' && busyUpdateState.hidden === 'false' && busyUpdateState.closeDisabled === true && busyUpdateState.dismissDisabled === false && busyUpdateState.dismissText === 'Download abbrechen' && busyUpdateState.dismissDanger === true && busyUpdateState.headerHidden === false);
check('Update progress exposes an accessible live value', busyUpdateState.progressLabel === 'Update-Fortschritt' && busyUpdateState.progressText === 'Download 50%');
check('Update progress shows downloaded size, total size, speed, and ETA', busyUpdateState.progressDetails === '40.0 MB / 100.0 MB · 8.0 MB/s · ETA 00:08');
check('Update download progress is green, wide, and places its status below the line', busyUpdateState.progressState === 'downloading' && busyUpdateState.progressColor === 'rgb(117, 211, 155)' && busyUpdateState.progressTrackWidth >= 390 && busyUpdateState.progressTextTop >= busyUpdateState.progressTrackBottom);
check('Busy update preserves the available version subtitle while progress stays below the bar', busyUpdateState.messageHidden === false && busyUpdateState.messageText === 'Update v9.9.9 verfügbar');
+12 -3
View File
@@ -103,6 +103,7 @@ test('buffered installer downloads yield between progress updates so the rendere
let preparationFinished = false;
let rendererObservedProgressBeforeFinish = false;
let rendererObservationScheduled = false;
let nowMs = 0;
try {
await prepareUpdate(value => {
@@ -121,6 +122,7 @@ test('buffered installer downloads yield between progress updates so the rendere
latestYmlUrl: 'https://update.invalid/latest.yml'
},
tempDir,
now: () => nowMs,
fetchImpl: async url => url.endsWith('latest.yml')
? {
ok: true,
@@ -132,9 +134,12 @@ test('buffered installer downloads yield between progress updates so the rendere
status: 200,
body: {
getReader: () => ({
read: async () => readerIndex < chunks.length
? { done: false, value: chunks[readerIndex++] }
: { done: true }
read: async () => {
nowMs += 1000;
return readerIndex < chunks.length
? { done: false, value: chunks[readerIndex++] }
: { done: true };
}
})
}
}
@@ -147,6 +152,10 @@ test('buffered installer downloads yield between progress updates so the rendere
progress.filter(value => value.stage === 'downloading').map(value => value.percent),
[25, 50, 75, 100]
);
assert.deepEqual(
progress.filter(value => value.stage === 'downloading').map(value => [value.bytesPerSecond, value.etaSeconds]),
[[32768, 3], [32768, 2], [32768, 1], [32768, 0]]
);
} finally {
fs.rmSync(tempDir, { recursive: true, force: true });
}