Skip to content

Commit 48cc5c9

Browse files
committed
Fix progress polling hover state regression in downloads tab
1 parent f8a6e8b commit 48cc5c9

2 files changed

Lines changed: 79 additions & 7 deletions

File tree

tests/test_downloads_tab_workflow_route.mjs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4259,6 +4259,45 @@ test('progress polling explicitly handles cancelling as a non-interactive state'
42594259
);
42604260
});
42614261

4262+
test('Missing Models download progress preserves hovered controls between polling updates', () => {
4263+
const renderDownloadSnapshot = extractMethod(
4264+
resolveDownloadMethodsSource,
4265+
'renderDownloadSnapshot'
4266+
);
4267+
const renderDownloadProgressGroupForMissing = extractMethod(
4268+
resolveDownloadMethodsSource,
4269+
'renderDownloadProgressGroupForMissing'
4270+
);
4271+
const patchDownloadProgressContent = extractMethod(
4272+
resolveDownloadMethodsSource,
4273+
'patchDownloadProgressContent'
4274+
);
4275+
const pollDownloadProgress = extractMethod(
4276+
resolveDownloadMethodsSource,
4277+
'pollDownloadProgress'
4278+
);
4279+
4280+
assert.match(renderDownloadSnapshot, /patchDownloadProgressContent\(/);
4281+
assert.doesNotMatch(renderDownloadSnapshot, /progressDiv\.innerHTML\s*=/);
4282+
assert.match(renderDownloadProgressGroupForMissing, /patchDownloadProgressContent\(/);
4283+
assert.doesNotMatch(renderDownloadProgressGroupForMissing, /progressDiv\.innerHTML\s*=/);
4284+
assert.match(
4285+
patchDownloadProgressContent,
4286+
/patchDownloadsPanelElement\(progressDiv,\s*nextProgressDiv\)/
4287+
);
4288+
4289+
const activeProgressBranch = pollDownloadProgress.match(
4290+
/if \(progress\.status === 'downloading'[\s\S]*?\} else if \(progress\.status === 'cancelling'\)/
4291+
)?.[0] || '';
4292+
const cancellingBranch = pollDownloadProgress.match(
4293+
/else if \(progress\.status === 'cancelling'\)[\s\S]*?\} else if \(progress\.status === 'completed'\)/
4294+
)?.[0] || '';
4295+
assert.ok(activeProgressBranch);
4296+
assert.ok(cancellingBranch);
4297+
assert.doesNotMatch(activeProgressBranch, /refreshLocalMatchesUiForMissing/);
4298+
assert.doesNotMatch(cancellingBranch, /refreshLocalMatchesUiForMissing/);
4299+
});
4300+
42624301
test('native Xet progress polling refreshes every 200 milliseconds', () => {
42634302
const getDownloadProgressPollDelay = eval(`(${extractMethod(resolveDownloadMethodsSource, 'getDownloadProgressPollDelay')})`);
42644303

web/resolver/actions/resolve_download_methods.js

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,25 @@ export const resolveDownloadMethods = {
12531253
`;
12541254
},
12551255

1256+
patchDownloadProgressContent(progressDiv, html) {
1257+
if (!progressDiv) return;
1258+
1259+
const nextHtml = String(html || '');
1260+
if (
1261+
typeof progressDiv.cloneNode !== 'function'
1262+
|| typeof this.patchDownloadsPanelElement !== 'function'
1263+
) {
1264+
if (progressDiv.innerHTML !== nextHtml) {
1265+
progressDiv.innerHTML = nextHtml;
1266+
}
1267+
return;
1268+
}
1269+
1270+
const nextProgressDiv = progressDiv.cloneNode(false);
1271+
nextProgressDiv.innerHTML = nextHtml;
1272+
this.patchDownloadsPanelElement(progressDiv, nextProgressDiv);
1273+
},
1274+
12561275
renderDownloadProgressGroupForMissing(missing, progressDiv, { includeDownloadId = '', includeSnapshot = null } = {}) {
12571276
if (!missing || !progressDiv) return false;
12581277

@@ -1271,11 +1290,24 @@ export const resolveDownloadMethods = {
12711290

12721291
progressDiv.classList.remove('mr-is-hidden');
12731292
progressDiv.classList.add('mr-is-visible');
1274-
progressDiv.innerHTML = `<div class="mr-download-progress-list">${items.join('')}</div>`;
1293+
this.patchDownloadProgressContent(
1294+
progressDiv,
1295+
`<div class="mr-download-progress-list">${items.join('')}</div>`
1296+
);
12751297
this.attachDownloadActionHandlers(progressDiv, includeDownloadId);
12761298
return true;
12771299
},
12781300

1301+
ensureSearchDownloadButtonIcon(downloadBtn) {
1302+
if (
1303+
!downloadBtn?.classList?.contains('search-download-btn')
1304+
|| downloadBtn.querySelector?.('svg')
1305+
) {
1306+
return;
1307+
}
1308+
downloadBtn.innerHTML = getSvgIcon('download');
1309+
},
1310+
12791311
updateDownloadButtonForSnapshot(downloadBtn, progress, status, shouldRenderProgress) {
12801312
if (!downloadBtn) return;
12811313

@@ -1293,7 +1325,7 @@ export const resolveDownloadMethods = {
12931325
? `Paused ${percentLabel}`.trim()
12941326
: (percentLabel ? `Downloading ${percentLabel}` : 'Starting download...'));
12951327
if (downloadBtn.classList.contains('search-download-btn')) {
1296-
downloadBtn.innerHTML = getSvgIcon('download');
1328+
this.ensureSearchDownloadButtonIcon(downloadBtn);
12971329
downloadBtn.setAttribute('data-tooltip', label);
12981330
downloadBtn.setAttribute('aria-label', label);
12991331
} else {
@@ -1307,7 +1339,7 @@ export const resolveDownloadMethods = {
13071339
downloadBtn.disabled = true;
13081340
downloadBtn.classList.remove('mr-is-success-action', 'mr-btn-primary');
13091341
if (downloadBtn.classList.contains('search-download-btn')) {
1310-
downloadBtn.innerHTML = getSvgIcon('download');
1342+
this.ensureSearchDownloadButtonIcon(downloadBtn);
13111343
downloadBtn.setAttribute('data-tooltip', 'Cancelling download...');
13121344
downloadBtn.setAttribute('aria-label', 'Cancelling download');
13131345
} else {
@@ -1317,7 +1349,7 @@ export const resolveDownloadMethods = {
13171349
downloadBtn.disabled = false;
13181350
downloadBtn.classList.remove('mr-is-success-action', 'mr-btn-primary');
13191351
if (downloadBtn.classList.contains('search-download-btn')) {
1320-
downloadBtn.innerHTML = getSvgIcon('download');
1352+
this.ensureSearchDownloadButtonIcon(downloadBtn);
13211353
downloadBtn.setAttribute('data-tooltip', 'Retry download');
13221354
downloadBtn.setAttribute('aria-label', 'Retry download');
13231355
} else {
@@ -1354,7 +1386,10 @@ export const resolveDownloadMethods = {
13541386
if (progressDiv) {
13551387
progressDiv.classList.remove('mr-is-hidden');
13561388
progressDiv.classList.add('mr-is-visible');
1357-
progressDiv.innerHTML = this.renderDownloadSnapshotMarkup(downloadId, snapshot);
1389+
this.patchDownloadProgressContent(
1390+
progressDiv,
1391+
this.renderDownloadSnapshotMarkup(downloadId, snapshot)
1392+
);
13581393
this.attachDownloadActionHandlers(progressDiv, downloadId);
13591394
}
13601395

@@ -1873,7 +1908,6 @@ export const resolveDownloadMethods = {
18731908

18741909
if (progress.status === 'downloading' || progress.status === 'starting' || progress.status === 'paused') {
18751910
this.renderDownloadSnapshot(downloadId, snapshot, { progressDiv, downloadBtn });
1876-
this.refreshLocalMatchesUiForMissing?.(missing);
18771911
if (typeof this.requestQueuePanelUpdate === 'function') {
18781912
this.requestQueuePanelUpdate();
18791913
} else {
@@ -1886,7 +1920,6 @@ export const resolveDownloadMethods = {
18861920

18871921
} else if (progress.status === 'cancelling') {
18881922
this.renderDownloadSnapshot(downloadId, snapshot, { progressDiv, downloadBtn });
1889-
this.refreshLocalMatchesUiForMissing?.(missing);
18901923
this.updateQueuePanel?.();
18911924
setTimeout(() => this.pollDownloadProgress(downloadId), 500);
18921925

0 commit comments

Comments
 (0)