diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index b5c2349..cee6f57 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -124,6 +124,61 @@ const resolverShellCssSource = fs.readFileSync( 'utf8' ); +test('progress renderers preserve their distinct percent and escaping contracts', () => { + const renderProgressBar = eval(`(${extractMethod(renderFormatMethodsSource, 'renderProgressBar')})`); + const getStatusBadge = eval(`(${extractMethod(renderFormatMethodsSource, 'getStatusBadge')})`); + const renderProgressSection = eval(`(${extractMethod(renderFormatMethodsSource, 'renderProgressSection')})`); + const renderAnalysisProgress = eval(`(${extractMethod(renderFormatMethodsSource, 'renderAnalysisProgress')})`); + const renderLoadedModelsProgress = eval(`(${extractMethod(renderFormatMethodsSource, 'renderLoadedModelsProgress')})`); + const dialog = { + renderProgressBar, + getStatusBadge, + renderProgressSection, + escapeHtml(value) { + return String(value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + }; + + const analysis = renderAnalysisProgress.call(dialog, { + current: 1, + total: 4, + message: 'Analyzing ', + model_name: 'Model ' + }); + assert.match(analysis, /Analyzing /); + assert.match(analysis, /Model <A>/); + assert.match(analysis, /1 \/ 4/); + assert.match(analysis, /width: 25%/); + + const loaded = renderLoadedModelsProgress.call(dialog, { + current: 1, + total: 4, + percent: 73.4, + message: 'Loading ', + node_type: 'Node ' + }); + assert.match(loaded, /Loading <step>/); + assert.match(loaded, /Node <A>/); + assert.match(loaded, /1 \/ 4/); + assert.match(loaded, /width: 73%/); + + const shared = renderProgressSection.call(dialog, { + statusLabel: 'Shared', + messageHtml: 'Shared message', + progressHtml: '
', + detailHtml: '
' + }); + assert.match(shared, /mr-badge-info">Shared/); + assert.match(shared, /Shared message/); + assert.match(shared, /shared-progress/); + assert.match(shared, /shared-detail/); +}); + test('Missing Models rows use fixed-size virtualization for smooth stable scrolling', () => { const rowRules = Array.from( resolverMainCssSource.matchAll(/#model-resolver-modal \.mr-missing-list-row\s*\{([^}]+)\}/g) diff --git a/web/resolver/utils/render_format_methods.js b/web/resolver/utils/render_format_methods.js index 3497218..1a71239 100644 --- a/web/resolver/utils/render_format_methods.js +++ b/web/resolver/utils/render_format_methods.js @@ -171,6 +171,19 @@ export const renderFormatMethods = { `; }, + renderProgressSection({ statusLabel, messageHtml = '', progressHtml = '', detailHtml = '' } = {}) { + return ` +
+
+ ${this.getStatusBadge(statusLabel, 'info')} + ${messageHtml} +
+ ${progressHtml} + ${detailHtml} +
+ `; + }, + renderAnalysisProgress(progress = {}) { const current = Number(progress.current) || 0; const total = Number(progress.total) || 0; @@ -179,16 +192,12 @@ export const renderFormatMethods = { const modelName = progress.model_name ? this.escapeHtml(String(progress.model_name)) : ''; const detail = total > 0 ? `${current} / ${total}` : 'Preparing...'; - return ` -
-
- ${this.getStatusBadge('Analyzing', 'info')} - ${message} -
- ${this.renderProgressBar(percent, detail, `${percent}%`)} - ${modelName ? `
${modelName}
` : ''} -
- `; + return this.renderProgressSection({ + statusLabel: 'Analyzing', + messageHtml: message, + progressHtml: this.renderProgressBar(percent, detail, `${percent}%`), + detailHtml: modelName ? `
${modelName}
` : '', + }); }, renderLoadedModelsProgress(progress = {}) { @@ -202,16 +211,12 @@ export const renderFormatMethods = { const detail = total > 0 ? `${current} / ${total}` : 'Preparing...'; const nodeType = progress.node_type ? this.escapeHtml(String(progress.node_type)) : ''; - return ` -
-
- ${this.getStatusBadge('Loading', 'info')} - ${this.escapeHtml(message)} -
- ${this.renderProgressBar(percent, detail, `${percent}%`)} - ${nodeType ? `
${nodeType}
` : ''} -
- `; + return this.renderProgressSection({ + statusLabel: 'Loading', + messageHtml: this.escapeHtml(message), + progressHtml: this.renderProgressBar(percent, detail, `${percent}%`), + detailHtml: nodeType ? `
${nodeType}
` : '', + }); }, patchLoadedModelsProgress(container, renderedHtml = '') {