import re import os import pytest from playwright.sync_api import expect from test.playwright.helpers.flow_steps import flow_params, require from test.playwright.helpers.auth_selectors import EMAIL_INPUT, PASSWORD_INPUT, SUBMIT_BUTTON from test.playwright.helpers.auth_waits import wait_for_login_complete from test.playwright.helpers.response_capture import capture_response from test.playwright.helpers.model_providers import ( open_user_settings, safe_close_modal, select_default_model, ) RESULT_TIMEOUT_MS = 15000 def step_01_open_login( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): api_key = os.getenv("ZHIPU_AI_API_KEY") if not api_key: pytest.skip("ZHIPU_AI_API_KEY not set; skipping model providers test.") email, password = seeded_user_credentials flow_state["api_key"] = api_key flow_state["email"] = email flow_state["password"] = password with step("open login page"): flow_page.goto(login_url, wait_until="domcontentloaded") flow_state["login_opened"] = True snap("login_opened") def step_02_login( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): require(flow_state, "login_opened", "email", "password") page = flow_page form, _ = active_auth_context() email_input = form.locator(EMAIL_INPUT) password_input = form.locator(PASSWORD_INPUT) with step("fill credentials"): expect(email_input).to_have_count(1) expect(password_input).to_have_count(1) email_input.fill(flow_state["email"]) password_input.fill(flow_state["password"]) password_input.blur() with step("submit login"): submit_button = form.locator(SUBMIT_BUTTON) expect(submit_button).to_have_count(1) auth_click(submit_button, "submit_login") with step("wait for login"): wait_for_login_complete(page, timeout_ms=RESULT_TIMEOUT_MS) flow_state["logged_in"] = True snap("home_loaded") def step_03_open_settings( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): require(flow_state, "logged_in") page = flow_page with step("open settings"): open_user_settings(page, base_url) flow_state["settings_open"] = True snap("settings_opened") def step_04_open_model_providers( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): require(flow_state, "settings_open") page = flow_page with step("open model providers"): model_nav = page.locator("[data-testid='settings-nav-model-providers']") expect(model_nav).to_have_count(1) model_nav.first.click() expect(page.locator("text=Set default models")).to_be_visible() flow_state["model_providers_open"] = True snap("model_providers_open") def step_05_filter_zhipu( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): require(flow_state, "model_providers_open") page = flow_page with step("filter providers"): search_input = page.locator("[data-testid='model-providers-search']") expect(search_input).to_have_count(1) search_input.first.fill("zhipu") available_section = page.locator("[data-testid='available-models-section']") provider = available_section.locator( "[data-testid='available-model-card'][data-provider='ZHIPU-AI']" ).first if provider.count() == 0: added_section = page.locator("[data-testid='added-models-section']") if ( added_section.locator( "[data-testid='added-model-card'][data-provider='ZHIPU-AI']" ).count() == 0 ): raise AssertionError("ZHIPU-AI provider not found in available or added models.") else: expect(provider).to_be_visible() flow_state["provider_filtered"] = True snap("provider_filtered") def step_06_add_api_key( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): require(flow_state, "provider_filtered", "api_key") page = flow_page available_section = page.locator("[data-testid='available-models-section']") provider = available_section.locator( "[data-testid='available-model-card'][data-provider='ZHIPU-AI']" ).first with step("add ZHIPU-AI api key"): if provider.count() > 0: provider.click() else: added_section = page.locator("[data-testid='added-models-section']") card = added_section.locator( "[data-testid='added-model-card'][data-provider='ZHIPU-AI']" ).first api_key_button = card.locator("button", has_text=re.compile("API-?Key", re.I)).first expect(api_key_button).to_be_visible() api_key_button.click() modal = page.locator("[data-testid='apikey-modal']") expect(modal).to_be_visible() api_input = modal.locator("[data-testid='apikey-input']").first save_button = modal.locator("[data-testid='apikey-save']").first try: def trigger(): api_input.fill(flow_state["api_key"]) save_button.click() capture_response( page, trigger, lambda resp: resp.request.method == "POST" and "/v1/llm/set_api_key" in resp.url, ) expect(modal).not_to_be_visible(timeout=RESULT_TIMEOUT_MS) except Exception: safe_close_modal(modal) raise with step("confirm added model"): added_section = page.locator("[data-testid='added-models-section']") expect(added_section).to_be_visible() expect( added_section.locator( "[data-testid='added-model-card'][data-provider='ZHIPU-AI']" ) ).to_be_visible() flow_state["provider_added"] = True snap("provider_saved") def step_07_set_defaults( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): require(flow_state, "provider_added") page = flow_page with step("set default models"): llm_combo = page.locator("[data-testid='default-llm-combobox']").first emb_combo = page.locator("[data-testid='default-embedding-combobox']").first select_default_model( page, expect, llm_combo, "glm-4-flash@ZHIPU-AI", "glm-4-flash", list_testid="default-llm-options", fallback_to_first=False, timeout_ms=RESULT_TIMEOUT_MS, ) selected_emb_text, _ = select_default_model( page, expect, emb_combo, "embedding-2@ZHIPU-AI", "embedding-2", list_testid="default-embedding-options", fallback_to_first=True, timeout_ms=RESULT_TIMEOUT_MS, ) flow_state["selected_emb_text"] = selected_emb_text flow_state["defaults_set"] = True snap("defaults_selected") def step_08_verify_persist( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): require(flow_state, "defaults_set") page = flow_page with step("reload and verify defaults"): page.reload(wait_until="domcontentloaded") expect(page.locator("text=Set default models")).to_be_visible() llm_combo = page.locator("[data-testid='default-llm-combobox']").first emb_combo = page.locator("[data-testid='default-embedding-combobox']").first expect(llm_combo).to_contain_text("glm-4-flash") expect(emb_combo).to_contain_text(flow_state.get("selected_emb_text") or "embedding-2") added_section = page.locator("[data-testid='added-models-section']") expect( added_section.locator( "[data-testid='added-model-card'][data-provider='ZHIPU-AI']" ) ).to_be_visible() snap("defaults_persisted") snap("success") STEPS = [ ("01_open_login", step_01_open_login), ("02_login", step_02_login), ("03_open_settings", step_03_open_settings), ("04_open_model_providers", step_04_open_model_providers), ("05_filter_zhipu", step_05_filter_zhipu), ("06_add_api_key", step_06_add_api_key), ("07_set_defaults", step_07_set_defaults), ("08_verify_persist", step_08_verify_persist), ] @pytest.mark.p1 @pytest.mark.auth @pytest.mark.parametrize("step_fn", flow_params(STEPS)) def test_add_zhipu_ai_set_defaults_persist_flow( step_fn, flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, ): step_fn( flow_page, flow_state, base_url, login_url, active_auth_context, step, snap, auth_click, seeded_user_credentials, )