diff --git a/static/boot.js b/static/boot.js index 398b24e77..ca640e327 100644 --- a/static/boot.js +++ b/static/boot.js @@ -2368,6 +2368,7 @@ window._applyTitlebarProfileVisibility=_applyTitlebarProfileVisibility; // after the saved session is visible. const _redirectBootModelDropdownIfUnauth=(res)=>{ if(!res||res.status!==401) return false; + window._modelDropdownReady=null; if(_bootActiveProfileUnauthRedirectBudget.isConsumed()) return true; if(_bootActiveProfileUnauthRedirectBudget.spendOnRedirect(sessionStorage)){ _bootActiveProfileUnauthRedirectBudget.redirectToLogin(window.location.pathname+window.location.search); diff --git a/tests/test_5021_boot_model_redirect_budget.py b/tests/test_5021_boot_model_redirect_budget.py index 47002c238..d6b1ae827 100644 --- a/tests/test_5021_boot_model_redirect_budget.py +++ b/tests/test_5021_boot_model_redirect_budget.py @@ -25,6 +25,7 @@ const fs = require('fs'); const bootSrc = fs.readFileSync(process.argv[2], 'utf8'); const uiSrc = fs.readFileSync(process.argv[3], 'utf8'); const scenario = JSON.parse(process.argv[4] || '{}'); +globalThis.window = globalThis; function extractBlock(source, startMarker, endMarker) { const start = source.indexOf(startMarker); @@ -224,10 +225,10 @@ const resolveBlock = extractBlock( ' async function _resolveActiveProfileBootstrapState({', ' // Fetch active profile' ); -const bootRedirectCallbackBlock = extractBlock( +const bootDropdownBlock = extractBlock( bootSrc, ' const _redirectBootModelDropdownIfUnauth=(res)=>{', - ' const _hydrateModelDropdown=({' + ' setTimeout(()=>{' ); const redirectIfUnauthLine = extractSingleLineFunction( uiSrc, @@ -240,10 +241,24 @@ eval(bootBlock.replace( ' globalThis._bootActiveProfileUnauthRedirectBudget=(()=>{' )); eval(resolveBlock); -eval(bootRedirectCallbackBlock.replace( +eval(bootDropdownBlock + .replace( ' const _redirectBootModelDropdownIfUnauth=(res)=>{', ' globalThis._redirectBootModelDropdownIfUnauth=(res)=>{' -)); + ) + .replace( + ' const _hydrateModelDropdown=({redirectIfUnauth=null}={})=>populateModelDropdown({', + ' globalThis._hydrateModelDropdown=({redirectIfUnauth=null}={})=>populateModelDropdown({' + ) + .replace( + ' const _startModelDropdown=()=>{', + ' globalThis._startModelDropdown=()=>{' + ) + .replace( + ' const _startBootModelDropdown=()=>{', + ' globalThis._startBootModelDropdown=()=>{' + ) +); eval(redirectIfUnauthLine.replace( 'function _redirectIfUnauth(res){', 'globalThis._redirectIfUnauth=function _redirectIfUnauth(res){' @@ -366,7 +381,8 @@ async function runBootAttempt(attempt, redirects, storage, fetchQueue, jsonCalls ok: false, json: async () => ({active_provider: 'openai', default_model: 'gpt-4o', configured_model_badges: {}, groups: []}), }); - await populateModelDropdown({preferProfileDefaultOnFreshBoot: true}); + await globalThis._startBootModelDropdown(); + await globalThis._ensureModelDropdownReady(); } else { throw new Error(`unknown scenario: ${scenario.kind}`); } @@ -458,4 +474,5 @@ def test_post_boot_model_refresh_keeps_normal_401_redirect(driver_path): assert payload["redirects"] == ["login?next=%2Fsession%2Fabc%3Fworkspace%3Dtest"] assert payload["jsonCalls"] == 0 + assert payload["storage"] == {} assert "window._ensureModelDropdownReady=_startModelDropdown;" in BOOT_JS.read_text(encoding="utf-8") diff --git a/tests/test_model_default_boot_precedence.py b/tests/test_model_default_boot_precedence.py index a94eed91e..d7b80d6c5 100644 --- a/tests/test_model_default_boot_precedence.py +++ b/tests/test_model_default_boot_precedence.py @@ -179,7 +179,8 @@ def test_boot_settings_applies_default_without_deleting_browser_model_state(): def test_boot_model_dropdown_explicitly_requests_profile_default_precedence(): - assert "populateModelDropdown({preferProfileDefaultOnFreshBoot:true})" in BOOT_JS + assert "const _hydrateModelDropdown=({redirectIfUnauth=null}={})=>populateModelDropdown({" in BOOT_JS + assert "preferProfileDefaultOnFreshBoot:true" in BOOT_JS # #2726 invariant: boot path must keep profile/server default ahead of stale # browser-persisted state when a default exists. Post-#2716 cherry-pick onto # post-stage-batch11 master uses `stateToApply` pattern rather than the