feat: summarize completed upload batches

Capture real batch duration and show a dedicated localized completion summary with success, failure, and skipped counts plus a direct action to open failed uploads.
This commit is contained in:
Sucukdeluxe
2026-08-21 01:10:53 +02:00
parent fb544efe2f
commit 04bd4b396c
10 changed files with 153 additions and 0 deletions
+1
View File
@@ -31,6 +31,7 @@ test('translations cover static labels and interpolated status text in both lang
assert.equal(translateText('Alle Status', 'en'), 'Any status');
assert.equal(translateText('Any status', 'de'), 'Alle Status');
assert.equal(translateText('Filter', 'en'), 'Filters');
assert.equal(translateText('Fehler anzeigen', 'en'), 'Show failures');
});
test('sidebar hierarchy uses distinct English and German kicker labels', () => {
+1
View File
@@ -179,6 +179,7 @@ test('header occupies its final geometry before asynchronous initialization', ()
assert.match(html, /class="update-progress-footer"[\s\S]*id="updateProgressDetails"[\s\S]*id="updateProgressSize"[\s\S]*id="updateProgressSpeed"[\s\S]*id="updateProgressEta"[\s\S]*id="updateProgressText"/u);
assert.match(html, /id="queueFilterResetBtn"[^>]*disabled[^>]*>Filter zurücksetzen</u);
assert.match(html, /class="queue-filter-summary"[\s\S]*id="queueActiveFilterCount"[^>]*>0</u);
assert.match(html, /id="batchCompletionToast"[^>]*hidden[\s\S]*id="batchCompletionText"[\s\S]*id="batchCompletionShowErrors"/u);
assert.doesNotMatch(html, /<\/div>\s*<div class="queue-filter-bar"/u);
assert.match(css, /\.queue-filter-bar\s*\{[^}]*display:\s*flex;[^}]*margin-left:\s*auto;[^}]*border:\s*1px solid var\(--border\);[^}]*border-radius:\s*7px;/su);
assert.match(css, /#updateProgressDetails\s*\{[^}]*display:\s*grid;[^}]*grid-template-columns:\s*19ch 2ch 11ch 2ch 10ch;[^}]*column-gap:\s*0;[^}]*font-variant-numeric:\s*tabular-nums;/su);
+24
View File
@@ -759,6 +759,30 @@ setTimeout(async () => {
})()\`);
check('Queue filter reset clears every filter and updates the stable active count', queueFilterReset.before.hidden === false && queueFilterReset.before.disabled === false && queueFilterReset.before.count === '4' && queueFilterReset.before.active === true && queueFilterReset.after.hidden === false && queueFilterReset.after.disabled === true && queueFilterReset.after.count === '0' && queueFilterReset.after.active === false && queueFilterReset.after.sidebar === 'all' && queueFilterReset.after.search === '' && queueFilterReset.after.hoster === '' && queueFilterReset.after.status === '' && queueFilterReset.after.allPressed === 'true' && queueFilterReset.after.visible === 'filter-a|filter-b');
const batchCompletionSummary = await wc.executeJavaScript(\`(async () => {
setUiLanguage('en');
queueJobs = [{ id: 'batch-error', file: 'C:/ui/error.bin', fileName: 'error.bin', hoster: 'byse.sx', status: 'error', error: 'Network failure', bytesUploaded: 0, bytesTotal: 100, progress: 0 }];
rebuildJobIndex();
renderQueueTable();
showBatchCompletionSummary({ total: 1237, succeeded: 1234, failed: 1, skipped: 2, durationSec: 754 });
await new Promise(resolve => requestAnimationFrame(resolve));
const toast = document.getElementById('batchCompletionToast');
const before = {
hidden: toast.hidden,
shown: toast.classList.contains('show'),
text: document.getElementById('batchCompletionText').textContent,
actionHidden: document.getElementById('batchCompletionShowErrors').hidden
};
document.getElementById('batchCompletionShowErrors').click();
const after = { filter: uploadSidebarFilter, shown: toast.classList.contains('show') };
setUiLanguage('de');
queueJobs = [];
rebuildJobIndex();
setUploadSidebarFilter('all');
return { before, after };
})()\`);
check('Batch completion summarizes results and opens the failed upload filter', batchCompletionSummary.before.hidden === false && batchCompletionSummary.before.shown === true && batchCompletionSummary.before.text === '1,234 successful · 1 failed · 2 skipped · 13 min' && batchCompletionSummary.before.actionHidden === false && batchCompletionSummary.after.filter === 'error' && batchCompletionSummary.after.shown === false);
const queueSelectionAnchor = await wc.executeJavaScript(\`(() => {
queueJobs = ['a', 'b', 'c', 'd'].map(id => ({ id: 'anchor-' + id, file: 'C:/ui/anchor-' + id + '.bin', fileName: 'anchor-' + id + '.bin', hoster: 'byse.sx', status: 'queued', bytesUploaded: 0, bytesTotal: 100, progress: 0 }));
selectedJobIds.clear();
+2
View File
@@ -159,6 +159,8 @@ describe('UploadManager', () => {
assert.equal(summary.total, 2);
assert.equal(summary.succeeded, 2);
assert.equal(summary.failed, 0);
assert.equal(Number.isInteger(summary.durationSec), true);
assert.equal(summary.durationSec >= 0, true);
assert.equal(summary.files.length, 2);
});