| 1 | from pathlib import Path |
| 2 | |
| 3 | |
| 4 | PROJECT_ROOT = Path(__file__).resolve().parents[1] |
| 5 | |
| 6 | |
| 7 | def test_oauth_settings_exposes_provider_cards_and_model_slots(): |
| 8 | config_html = (PROJECT_ROOT / "plugins/_oauth/webui/config.html").read_text(encoding="utf-8") |
| 9 | store_js = (PROJECT_ROOT / "plugins/_oauth/webui/oauth-config-store.js").read_text(encoding="utf-8") |
| 10 | |
| 11 | assert "providerCards" in config_html + store_js |
| 12 | assert "OAuth Connections" in config_html + store_js |
| 13 | assert "OAUTH_PROVIDERS" not in store_js |
| 14 | assert "PROVIDER_FALLBACKS" not in store_js |
| 15 | assert "Connected accounts" not in config_html |
| 16 | assert "Choose your models" in config_html |
| 17 | assert "Main model" in store_js |
| 18 | assert "Utility model" in store_js |
| 19 | assert "provider_map" in store_js |
| 20 | assert "<h3>Accounts</h3>" not in config_html |
| 21 | assert "oauth-plan-catalog" not in config_html |
| 22 | assert "oauth-model-provider-field" in config_html |
| 23 | assert "slotProviderChoices(slot.key)" in config_html |
| 24 | assert "<span>Provider</span>" not in config_html |
| 25 | assert ':aria-label="`${slot.title} provider`"' in config_html |
| 26 | assert "connectedProviderCards().length" in config_html |
| 27 | assert "useProviderForSlot(slot.key, $event.target.value)" in config_html |
| 28 | assert ":value=\"$store.oauthConfig.slotCanUseModels(slot.key) ? $store.oauthConfig.modelSlot(slot.key).provider : ''\"" in config_html |
| 29 | assert "slotCanUseModels(slot.key)" in config_html |
| 30 | assert "<span>Account</span>" not in config_html |
| 31 | assert "oauth-connected-panel" not in config_html |
| 32 | assert "oauth-provider-usage" in config_html |
| 33 | assert "usageWindows(card.provider_id)" in config_html |
| 34 | assert "connectedUsageWindows()" not in config_html |
| 35 | assert "Agent Zero models" not in config_html |
| 36 | assert "Usage plans" not in config_html |
| 37 | assert "usagePlanEntries" in store_js |
| 38 | assert "usage_plan_catalog" in (PROJECT_ROOT / "plugins/_oauth/helpers/summary.py").read_text(encoding="utf-8") |
| 39 | assert "copyMainToUtility" in config_html + store_js |
| 40 | assert "connectedSummaryLabel()" not in config_html |
| 41 | |
| 42 | |
| 43 | def test_oauth_settings_exposes_provider_specific_controls_and_generic_copy(): |
| 44 | config_html = (PROJECT_ROOT / "plugins/_oauth/webui/config.html").read_text(encoding="utf-8") |
| 45 | store_js = (PROJECT_ROOT / "plugins/_oauth/webui/oauth-config-store.js").read_text(encoding="utf-8") |
| 46 | |
| 47 | assert "Check models" not in config_html |
| 48 | assert "card.connected ? 'Connected' : 'Available'" not in config_html |
| 49 | assert '<span class="oauth-account-state connected" x-show="card.connected">Connected</span>' in config_html |
| 50 | assert "Select account-backed models used by the Main model and Utility model slots." not in config_html |
| 51 | assert "Primary model for chat, reasoning, and browser tasks." not in config_html + store_js |
| 52 | assert "Background model for summaries, memory, and prompt preparation." not in config_html + store_js |
| 53 | assert '@click="$store.oauthConfig.openModelDropdown(slot.key, $el)"' in config_html |
| 54 | assert "top: calc(100% + 4px);" in config_html |
| 55 | assert "enterprise_domain" in config_html + store_js |
| 56 | assert "manualCallback" in config_html + store_js |
| 57 | assert "Paste callback URL, query string, or code" in config_html + store_js |
| 58 | assert "supports_enterprise_domain" in config_html + store_js |
| 59 | assert "supports_manual_callback" in config_html + store_js |
| 60 | assert "supports_oauth_client_config" in config_html + store_js |
| 61 | assert "supports_quota_project" in config_html + store_js |
| 62 | assert "OAuth client ID" in config_html |
| 63 | assert "quota_project_id" in config_html + store_js |
| 64 | assert "Codex response defaults" in config_html |
| 65 | assert "$store.oauthConfig.codex().reasoning_effort" in config_html |
| 66 | assert "$store.oauthConfig.codex().reasoning_summary" in config_html |
| 67 | assert "$store.oauthConfig.codex().text_verbosity" in config_html |
| 68 | assert "providerDetailOpen(card.provider_id)" in config_html + store_js |
| 69 | assert "providerDevice(card.provider_id)?.user_code" in config_html |
| 70 | assert "submitManualCallback(card.provider_id)" in config_html |
| 71 | assert "cancelConnect(card.provider_id)" in config_html |
| 72 | assert "selectedProvider()" not in config_html |
| 73 | assert "oauth-auth-attempt" in config_html |
| 74 | assert "oauth-provider-row-detail" in config_html |
| 75 | assert "oauth-provider-detail" not in config_html |
| 76 | assert "oauth-detail-metrics" not in config_html |
| 77 | assert "Codex/ChatGPT Account" not in config_html + store_js |
| 78 | assert "activeModelsDescription()" in config_html + store_js |
| 79 | assert "activeProviderModels()" in config_html + store_js |
| 80 | assert "Available models from Codex account" not in config_html |
| 81 | assert "disconnectProvider(card.provider_id)" in config_html |
| 82 | assert "disconnectProvider($store.oauthConfig.selectedProviderId)" not in config_html |
| 83 | assert "color: #35d07f;" not in config_html |
| 84 | assert ".oauth .oauth-provider-row.connected .oauth-provider-row-copy span" in config_html |
| 85 | assert ".oauth .oauth-provider-row.connected .oauth-usage-head strong" in config_html |
| 86 | assert ".oauth .oauth-provider-row.connected .oauth-usage-window p" in config_html |
| 87 | assert "var(--color-text-secondary, rgba(255, 255, 255, .72))" in config_html |
| 88 | |
| 89 | |
| 90 | def test_oauth_connect_buttons_disable_during_any_provider_connection(): |
| 91 | config_html = (PROJECT_ROOT / "plugins/_oauth/webui/config.html").read_text(encoding="utf-8") |
| 92 | store_js = (PROJECT_ROOT / "plugins/_oauth/webui/oauth-config-store.js").read_text(encoding="utf-8") |
| 93 | |
| 94 | assert "providerPrimaryDisabled" in config_html + store_js |
| 95 | assert "this.connectingProvider || this.disconnectingProvider" in store_js |
| 96 | assert ':disabled="Boolean($store.oauthConfig.connectingProvider) || $store.oauthConfig.disconnectingProvider"' not in config_html |
| 97 | assert "cancelConnect(providerId = \"\")" in store_js |
| 98 | assert "if (this.connectingProvider === providerId) this.connectingProvider = \"\";" in store_js |
| 99 | |
| 100 | |
| 101 | def test_oauth_available_models_list_sits_above_advanced_without_borders(): |
| 102 | config_html = (PROJECT_ROOT / "plugins/_oauth/webui/config.html").read_text(encoding="utf-8") |
| 103 | |
| 104 | assert "activeModelsDescription()" in config_html |
| 105 | assert config_html.index("<h3>Providers</h3>") < config_html.index("Choose your models") |
| 106 | assert config_html.index("<h3>Providers</h3>") < config_html.index("<summary>Advanced</summary>") |
| 107 | assert config_html.index("Available models") < config_html.index("<summary>Advanced</summary>") |
| 108 | assert ".oauth-models-panel {\n display: grid;\n gap: 10px;\n padding: 0;\n border: 0;\n }" in config_html |
| 109 | assert ".oauth-provider-list {\n display: grid;\n gap: 12px;\n padding: 0;\n border: 0;" in config_html |
| 110 | assert ".oauth-provider-row-detail {\n display: grid;\n grid-column: 1 / -1;" in config_html |
| 111 | assert ".oauth-advanced {\n border: 0;\n border-radius: 0;\n padding: 0;\n }" in config_html |
| 112 | model_chip_rule = config_html.split(".oauth-models span {", 1)[1].split("}", 1)[0] |
| 113 | assert "border:" not in model_chip_rule |
| 114 | |
| 115 | |
| 116 | def test_oauth_model_slots_reuse_model_config_api(): |
| 117 | store_js = (PROJECT_ROOT / "plugins/_oauth/webui/oauth-config-store.js").read_text(encoding="utf-8") |
| 118 | |
| 119 | assert 'import { store as modelConfigStore } from "/plugins/_model_config/webui/model-config-store.js";' in store_js |
| 120 | assert 'const MODEL_CONFIG_API = "/plugins/_model_config";' in store_js |
| 121 | assert "model_config_get" in store_js |
| 122 | assert "model_config_set" in store_js |
| 123 | assert "isOauthProvider" in store_js |
| 124 | assert "providerCards()" in store_js |
| 125 | assert "saveModelConfigIfDirty" in store_js |
| 126 | assert "slotProviderChoices()" in store_js |
| 127 | assert "modelProviderOptionLabel(provider)" in store_js |
| 128 | assert "return this.connectedProviderCards();" in store_js |
| 129 | assert "if (!this.providerConnected(providerId)) return;" in store_js |
| 130 | assert "const providerId = slot.provider;" in store_js |
| 131 | assert "const providerId = this.isOauthProvider(this.activeModelProvider)" not in store_js |
| 132 | assert "applySoleConnectedProviderDefaults" in store_js |
| 133 | assert "this.applySoleConnectedProviderDefaults();" in store_js |
| 134 | assert "if (providers.length !== 1 || !this.modelConfig) return;" in store_js |
| 135 | assert "if (model.provider && model.provider !== providerId) continue;" in store_js |
| 136 | assert "model.name = \"\";" in store_js |
| 137 | assert "this.modelSlotCurrentProviders[key] ?? slot.provider" in store_js |
| 138 | assert "currentChatModelConfigured" not in store_js |
| 139 | assert "providerDefaultModel" not in store_js |
| 140 | assert 'new CustomEvent("model-setup-changed"' in store_js |
| 141 | assert 'detail: { source: "_oauth", providerId }' in store_js |
| 142 | assert "await this.handleProviderConnected(providerId)" in store_js |
| 143 | |
| 144 | |
| 145 | def test_browser_callback_completion_is_observed_from_modal(): |
| 146 | store_js = (PROJECT_ROOT / "plugins/_oauth/webui/oauth-config-store.js").read_text(encoding="utf-8") |
| 147 | |
| 148 | assert "startCallbackPolling(providerId)" in store_js |
| 149 | assert "stopCallbackPolling(providerId)" in store_js |
| 150 | assert "this.providerConnected(providerId)" in store_js |
| 151 | assert "await this.handleProviderConnected(providerId, { statusLoaded: true })" in store_js |
| 152 | |
| 153 | |
| 154 | def test_device_polling_honors_provider_interval_updates(): |
| 155 | store_js = (PROJECT_ROOT / "plugins/_oauth/webui/oauth-config-store.js").read_text(encoding="utf-8") |
| 156 | |
| 157 | start_polling = store_js.split("startPolling(providerId = CODEX_PROVIDER)", 1)[1].split( |
| 158 | "pollProvider(providerId = CODEX_PROVIDER)", |
| 159 | 1, |
| 160 | )[0] |
| 161 | assert "window.setTimeout(tick, delayMs)" in start_polling |
| 162 | assert "window.setInterval(tick" not in start_polling |
| 163 | assert "void tick();" not in start_polling |
| 164 | assert "interval: response.interval || device.interval" in start_polling |
| 165 | assert "expires_at: response.expires_at || device.expires_at" in start_polling |
| 166 | assert "Date.now() / 1000 > expiresAt" in start_polling |
| 167 | |
| 168 | |
| 169 | def test_usage_plan_catalog_stays_backend_only_on_oauth_settings_page(): |
| 170 | config_html = (PROJECT_ROOT / "plugins/_oauth/webui/config.html").read_text(encoding="utf-8") |
| 171 | store_js = (PROJECT_ROOT / "plugins/_oauth/webui/oauth-config-store.js").read_text(encoding="utf-8") |
| 172 | plans_py = (PROJECT_ROOT / "plugins/_oauth/helpers/usage_plans.py").read_text(encoding="utf-8") |
| 173 | |
| 174 | assert "oauth-plan-catalog" not in config_html |
| 175 | assert "usagePlanEntries" in store_js |
| 176 | assert "Google Cloud Gemini" in plans_py |
| 177 | assert "Google Cloud project" in plans_py |
| 178 | assert "GEMINI_API_PROVIDER_ID" in plans_py |
| 179 | assert "Google Gemini / Antigravity" not in plans_py |
| 180 | assert "Claude Code" not in plans_py |
| 181 | assert "antigravity_subscription_oauth" not in plans_py |
| 182 | assert "claude_code_oauth" not in plans_py |
| 183 | |
| 184 | |
| 185 | def test_oauth_model_wrappers_do_not_add_box_borders_or_lateral_padding(): |
| 186 | config_html = (PROJECT_ROOT / "plugins/_oauth/webui/config.html").read_text(encoding="utf-8") |
| 187 | |
| 188 | assert ".oauth-model-config {\n display: grid;\n gap: 12px;\n padding: 0;\n border: 0;\n }" in config_html |
| 189 | assert ".oauth-model-card {\n display: grid;" in config_html |
| 190 | assert " padding: 0;\n border: 0;\n background: transparent;" in config_html |
| 191 | assert "oauth-model-card.is-codex" not in config_html |
| 192 | assert "'is-codex'" not in config_html |
| 193 | |
| 194 | |
| 195 | def test_oauth_discovery_card_renders_in_welcome_account_panel(): |
| 196 | discovery_cards = ( |
| 197 | PROJECT_ROOT |
| 198 | / "plugins/_discovery/extensions/python/banners/10_discovery_cards.py" |
| 199 | ).read_text(encoding="utf-8") |
| 200 | welcome_cards = ( |
| 201 | PROJECT_ROOT |
| 202 | / "plugins/_discovery/extensions/webui/welcome-actions-end/discovery-cards.html" |
| 203 | ).read_text(encoding="utf-8") |
| 204 | discovery_store = (PROJECT_ROOT / "plugins/_discovery/webui/discovery-store.js").read_text(encoding="utf-8") |
| 205 | |
| 206 | assert "discovery-oauth-accounts" in discovery_cards |
| 207 | assert "Your AI accounts" in discovery_cards |
| 208 | assert "Use your subscription-backed logins for model access." in discovery_cards |
| 209 | assert "Link account-backed providers such as" not in discovery_cards |
| 210 | assert "Connected OAuth accounts" not in discovery_cards |
| 211 | assert "discovery-codex-oauth" not in discovery_cards |
| 212 | assert "5h and weekly limits are ready." not in discovery_cards |
| 213 | assert "account providers connected and ready for model selection" not in discovery_cards |
| 214 | assert '"icon": "account_circle"' not in discovery_cards |
| 215 | assert "usage_windows" in discovery_cards |
| 216 | assert "account_chips" in discovery_cards |
| 217 | assert "discovery-account-card" in welcome_cards |
| 218 | assert "discovery-account-chip" in welcome_cards |
| 219 | assert "discovery-account-icon" not in welcome_cards |
| 220 | assert ".discovery-account-chip {\n display: inline-grid;" in welcome_cards |
| 221 | assert " border-radius: 8px;" in welcome_cards |
| 222 | assert "discovery-account-usage" in welcome_cards |
| 223 | assert "formatRemainingPercent(window)" in welcome_cards |
| 224 | assert "formatRemainingPercent(window)" in discovery_store |
| 225 | assert "usageWidth(window)" in discovery_store |
| 226 | assert "oauthAccountCards" in discovery_store |
| 227 | assert 'card.id === "discovery-oauth-accounts"' in discovery_store |