import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts";import { EnvironmentId } from "@t3tools/contracts";import { createModelCapabilities } from "@t3tools/shared/model";import { page, userEvent } from "vitest/browser";import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";import { render } from "vitest-browser-react"; import { ProviderModelPicker } from "./ProviderModelPicker";import { getCustomModelOptionsByInstance } from "../../modelSelection";import { deriveProviderInstanceEntries, sortProviderInstanceEntries,} from "../../providerInstances";import type { ModelEsque } from "./providerIconUtils";import { DEFAULT_CLIENT_SETTINGS, DEFAULT_UNIFIED_SETTINGS, type UnifiedSettings,} from "@t3tools/contracts/settings";import { __resetLocalApiForTests } from "../../localApi"; // Mock the environments/runtime module to provide a mock primary environment connectionvi.mock("../../environments/runtime", () => { const primaryConnection = { kind: "primary" as const, knownEnvironment: { id: "environment-local", label: "Local environment", source: "manual" as const, environmentId: EnvironmentId.make("environment-local"), target: { httpBaseUrl: "http://localhost:3000", wsBaseUrl: "ws://localhost:3000", }, }, environmentId: EnvironmentId.make("environment-local"), client: { server: { getConfig: vi.fn(), updateSettings: vi.fn(), }, }, ensureBootstrapped: async () => undefined, reconnect: async () => undefined, dispose: async () => undefined, }; return { getEnvironmentHttpBaseUrl: () => "http://localhost:3000", getSavedEnvironmentRecord: () => null, getSavedEnvironmentRuntimeState: () => null, hasSavedEnvironmentRegistryHydrated: () => true, listSavedEnvironmentRecords: () => [], resetSavedEnvironmentRegistryStoreForTests: vi.fn(), resetSavedEnvironmentRuntimeStoreForTests: vi.fn(), resolveEnvironmentHttpUrl: (_environmentId: unknown, path: string) => new URL(path, "http://localhost:3000").toString(), waitForSavedEnvironmentRegistryHydration: async () => undefined, addSavedEnvironment: vi.fn(), disconnectSavedEnvironment: vi.fn(), ensureEnvironmentConnectionBootstrapped: async () => undefined, getPrimaryEnvironmentConnection: () => primaryConnection, readEnvironmentConnection: () => primaryConnection, reconnectSavedEnvironment: vi.fn(), removeSavedEnvironment: vi.fn(), requireEnvironmentConnection: () => primaryConnection, resetEnvironmentServiceForTests: vi.fn(), startEnvironmentConnectionService: vi.fn(), subscribeEnvironmentConnections: () => () => {}, useSavedEnvironmentRegistryStore: ( selector: (state: { byId: Record<string, never> }) => unknown, ) => selector({ byId: {} }), useSavedEnvironmentRuntimeStore: ( selector: (state: { byId: Record<string, never> }) => unknown, ) => selector({ byId: {} }), };}); function selectDescriptor( id: string, label: string, options: ReadonlyArray<{ id: string; label: string; isDefault?: boolean }>,) { return { id, label, type: "select" as const, options: [...options], ...(options.find((option) => option.isDefault)?.id ? { currentValue: options.find((option) => option.isDefault)?.id } : {}), };} function booleanDescriptor(id: string, label: string) { return { id, label, type: "boolean" as const, };} const TEST_PROVIDERS: ReadonlyArray<ServerProvider> = [ { driver: ProviderDriverKind.make("codex"), instanceId: ProviderInstanceId.make("codex"), displayName: "Codex", enabled: true, installed: true, version: "0.116.0", status: "ready", auth: { status: "authenticated" }, checkedAt: new Date().toISOString(), slashCommands: [], skills: [], models: [ { slug: "gpt-5-codex", name: "GPT-5 Codex", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("reasoningEffort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, ]), booleanDescriptor("fastMode", "Fast Mode"), ], }), }, { slug: "gpt-5.3-codex", name: "GPT-5.3 Codex", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("reasoningEffort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, ]), booleanDescriptor("fastMode", "Fast Mode"), ], }), }, ], }, { driver: ProviderDriverKind.make("claudeAgent"), instanceId: ProviderInstanceId.make("claudeAgent"), displayName: "Claude", enabled: true, installed: true, version: "1.0.0", status: "ready", auth: { status: "authenticated" }, checkedAt: new Date().toISOString(), slashCommands: [], skills: [], models: [ { slug: "claude-opus-4-6", name: "Claude Opus 4.6", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("effort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, { id: "max", label: "max" }, ]), booleanDescriptor("thinking", "Thinking"), ], }), }, { slug: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("effort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, { id: "max", label: "max" }, ]), booleanDescriptor("thinking", "Thinking"), ], }), }, { slug: "claude-haiku-4-5", name: "Claude Haiku 4.5", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("effort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, ]), booleanDescriptor("thinking", "Thinking"), ], }), }, ], },]; const CODEX_INSTANCE_ID = ProviderInstanceId.make("codex");const CLAUDE_INSTANCE_ID = ProviderInstanceId.make("claudeAgent");const OPENCODE_INSTANCE_ID = ProviderInstanceId.make("opencode"); function buildCodexProvider(models: ServerProvider["models"]): ServerProvider { return { driver: ProviderDriverKind.make("codex"), instanceId: ProviderInstanceId.make("codex"), displayName: "Codex", enabled: true, installed: true, version: "0.116.0", status: "ready", auth: { status: "authenticated" }, checkedAt: new Date().toISOString(), models, slashCommands: [], skills: [], };} function buildOpenCodeProvider(models: ServerProvider["models"]): ServerProvider { return { driver: ProviderDriverKind.make("opencode"), instanceId: ProviderInstanceId.make("opencode"), enabled: true, installed: true, version: "1.0.0", status: "ready", auth: { status: "authenticated" }, checkedAt: new Date().toISOString(), models, slashCommands: [], skills: [], };} async function mountPicker(props: { activeInstanceId?: ProviderInstanceId; model: string; lockedProvider: ProviderDriverKind | null; lockedContinuationGroupKey?: string | null; providers?: ReadonlyArray<ServerProvider>; settings?: UnifiedSettings; triggerVariant?: "ghost" | "outline";}) { const host = document.createElement("div"); document.body.append(host); const onInstanceModelChange = vi.fn(); const providers = props.providers ?? TEST_PROVIDERS; const instanceEntries = sortProviderInstanceEntries(deriveProviderInstanceEntries(providers)); const activeInstanceId = props.activeInstanceId ?? CODEX_INSTANCE_ID; const modelOptionsByInstance = getCustomModelOptionsByInstance( props.settings ?? DEFAULT_UNIFIED_SETTINGS, providers, activeInstanceId, props.model, ); const screen = await render( <ProviderModelPicker activeInstanceId={activeInstanceId} model={props.model} lockedProvider={props.lockedProvider} lockedContinuationGroupKey={props.lockedContinuationGroupKey ?? null} instanceEntries={instanceEntries} modelOptionsByInstance={modelOptionsByInstance} triggerVariant={props.triggerVariant} onInstanceModelChange={onInstanceModelChange} />, { container: host }, ); return { onInstanceModelChange, // Back-compat alias used by callers that still assert on the old callback // name. Delegates to the instance-aware mock so existing expectations work. get onProviderModelChange() { return onInstanceModelChange; }, cleanup: async () => { await screen.unmount(); host.remove(); }, };} function getModelPickerListElement() { const modelPickerList = document.querySelector<HTMLElement>(".model-picker-list"); expect(modelPickerList).not.toBeNull(); return modelPickerList!;} function getModelPickerListText() { return getModelPickerListElement().textContent ?? "";} function getVisibleModelNames() { return Array.from(getModelPickerListElement().querySelectorAll<HTMLDivElement>("div.font-medium")) .map((element) => element.textContent?.replace(/New$/u, "").trim() ?? "") .filter((text) => text.length > 0);} function getSidebarProviderOrder() { return Array.from(document.querySelectorAll<HTMLElement>("[data-model-picker-provider]")).map( (element) => element.dataset.modelPickerProvider ?? "", );} describe("ProviderModelPicker", () => { beforeEach(async () => { // Reset test environment before each test await __resetLocalApiForTests(); }); afterEach(async () => { document.body.innerHTML = ""; await __resetLocalApiForTests(); }); it("shows provider sidebar in unlocked mode", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, }); try { await page.getByRole("button").click(); await vi.waitFor(() => { const text = document.body.textContent ?? ""; expect(text).not.toContain("Codex"); expect(text).toContain("Claude"); expect(text).toContain("Claude Opus 4.6"); }); } finally { await mounted.cleanup(); } }); it("shows favorites first in the provider sidebar", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, }); try { await page.getByRole("button").click(); await vi.waitFor(() => { expect(getSidebarProviderOrder().slice(0, 3)).toEqual([ "favorites", "codex", "claudeAgent", ]); }); } finally { await mounted.cleanup(); } }); it("filters models by selected provider in sidebar", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, }); try { await page.getByRole("button").click(); // Start with Claude models visible await vi.waitFor(() => { const text = document.body.textContent ?? ""; expect(text).not.toContain("GPT-5 Codex"); expect(text).toContain("Claude Opus 4.6"); }); // Click on Codex provider in sidebar await vi.waitFor(() => { expect(document.querySelector('[data-model-picker-provider="codex"]')).not.toBeNull(); }); await page.getByRole("button", { name: "Codex", exact: true }).click(); // Now should only show Codex models await vi.waitFor(() => { const listText = getModelPickerListText(); expect(listText).toContain("GPT-5 Codex"); expect(listText).not.toContain("Claude Opus 4.6"); }); } finally { await mounted.cleanup(); } }); it("uses client model visibility and ordering preferences", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, settings: { ...DEFAULT_UNIFIED_SETTINGS, providerModelPreferences: { [CLAUDE_INSTANCE_ID]: { hiddenModels: ["claude-opus-4-6"], modelOrder: ["claude-haiku-4-5", "claude-sonnet-4-6"], }, }, }, }); try { await page.getByRole("button").click(); await vi.waitFor(() => { expect(getVisibleModelNames()).toEqual(["Claude Haiku 4.5", "Claude Sonnet 4.6"]); expect(getModelPickerListText()).not.toContain("Claude Opus 4.6"); }); } finally { await mounted.cleanup(); } }); it("focuses the search input after selecting a sidebar provider", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, }); try { await page.getByRole("button").click(); await vi.waitFor(() => { expect(document.querySelector('[data-model-picker-provider="codex"]')).not.toBeNull(); }); await page.getByRole("button", { name: "Codex", exact: true }).click(); await vi.waitFor(() => { const searchInput = document.querySelector<HTMLInputElement>( 'input[placeholder="Search models..."]', ); expect(searchInput).not.toBeNull(); expect(document.activeElement).toBe(searchInput); }); } finally { await mounted.cleanup(); } }); it("shows locked provider header and only its models in locked mode", async () => { localStorage.setItem( "t3code:client-settings:v1", JSON.stringify({ ...DEFAULT_CLIENT_SETTINGS, favorites: [ { provider: "codex", model: "gpt-5-codex" }, { provider: "claudeAgent", model: "claude-sonnet-4-6" }, ], }), ); const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: ProviderDriverKind.make("claudeAgent"), }); try { await page.getByRole("button").click(); await vi.waitFor(() => { const text = document.body.textContent ?? ""; // Should show locked provider label expect(text).toContain("Claude"); expect(getVisibleModelNames()).toEqual([ "Claude Sonnet 4.6", "Claude Opus 4.6", "Claude Haiku 4.5", ]); }); } finally { localStorage.removeItem("t3code:client-settings:v1"); await mounted.cleanup(); } }); Lines 1–500 of 1226. Load more
import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts"; import { EnvironmentId } from "@t3tools/contracts"; import { createModelCapabilities } from "@t3tools/shared/model"; import { page, userEvent } from "vitest/browser"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { render } from "vitest-browser-react"; import { ProviderModelPicker } from "./ProviderModelPicker"; import { getCustomModelOptionsByInstance } from "../../modelSelection"; import { deriveProviderInstanceEntries, sortProviderInstanceEntries, } from "../../providerInstances"; import type { ModelEsque } from "./providerIconUtils"; import { DEFAULT_CLIENT_SETTINGS, DEFAULT_UNIFIED_SETTINGS, type UnifiedSettings, } from "@t3tools/contracts/settings"; import { __resetLocalApiForTests } from "../../localApi"; // Mock the environments/runtime module to provide a mock primary environment connection vi.mock("../../environments/runtime", () => { const primaryConnection = { kind: "primary" as const, knownEnvironment: { id: "environment-local", label: "Local environment", source: "manual" as const, environmentId: EnvironmentId.make("environment-local"), target: { httpBaseUrl: "http://localhost:3000", wsBaseUrl: "ws://localhost:3000", }, }, environmentId: EnvironmentId.make("environment-local"), client: { server: { getConfig: vi.fn(), updateSettings: vi.fn(), }, }, ensureBootstrapped: async () => undefined, reconnect: async () => undefined, dispose: async () => undefined, }; return { getEnvironmentHttpBaseUrl: () => "http://localhost:3000", getSavedEnvironmentRecord: () => null, getSavedEnvironmentRuntimeState: () => null, hasSavedEnvironmentRegistryHydrated: () => true, listSavedEnvironmentRecords: () => [], resetSavedEnvironmentRegistryStoreForTests: vi.fn(), resetSavedEnvironmentRuntimeStoreForTests: vi.fn(), resolveEnvironmentHttpUrl: (_environmentId: unknown, path: string) => new URL(path, "http://localhost:3000").toString(), waitForSavedEnvironmentRegistryHydration: async () => undefined, addSavedEnvironment: vi.fn(), disconnectSavedEnvironment: vi.fn(), ensureEnvironmentConnectionBootstrapped: async () => undefined, getPrimaryEnvironmentConnection: () => primaryConnection, readEnvironmentConnection: () => primaryConnection, reconnectSavedEnvironment: vi.fn(), removeSavedEnvironment: vi.fn(), requireEnvironmentConnection: () => primaryConnection, resetEnvironmentServiceForTests: vi.fn(), startEnvironmentConnectionService: vi.fn(), subscribeEnvironmentConnections: () => () => {}, useSavedEnvironmentRegistryStore: ( selector: (state: { byId: Record<string, never> }) => unknown, ) => selector({ byId: {} }), useSavedEnvironmentRuntimeStore: ( selector: (state: { byId: Record<string, never> }) => unknown, ) => selector({ byId: {} }), }; }); function selectDescriptor( id: string, label: string, options: ReadonlyArray<{ id: string; label: string; isDefault?: boolean }>, ) { return { id, label, type: "select" as const, options: [...options], ...(options.find((option) => option.isDefault)?.id ? { currentValue: options.find((option) => option.isDefault)?.id } : {}), }; } function booleanDescriptor(id: string, label: string) { return { id, label, type: "boolean" as const, }; } const TEST_PROVIDERS: ReadonlyArray<ServerProvider> = [ { driver: ProviderDriverKind.make("codex"), instanceId: ProviderInstanceId.make("codex"), displayName: "Codex", enabled: true, installed: true, version: "0.116.0", status: "ready", auth: { status: "authenticated" }, checkedAt: new Date().toISOString(), slashCommands: [], skills: [], models: [ { slug: "gpt-5-codex", name: "GPT-5 Codex", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("reasoningEffort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, ]), booleanDescriptor("fastMode", "Fast Mode"), ], }), }, { slug: "gpt-5.3-codex", name: "GPT-5.3 Codex", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("reasoningEffort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, ]), booleanDescriptor("fastMode", "Fast Mode"), ], }), }, ], }, { driver: ProviderDriverKind.make("claudeAgent"), instanceId: ProviderInstanceId.make("claudeAgent"), displayName: "Claude", enabled: true, installed: true, version: "1.0.0", status: "ready", auth: { status: "authenticated" }, checkedAt: new Date().toISOString(), slashCommands: [], skills: [], models: [ { slug: "claude-opus-4-6", name: "Claude Opus 4.6", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("effort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, { id: "max", label: "max" }, ]), booleanDescriptor("thinking", "Thinking"), ], }), }, { slug: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("effort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, { id: "max", label: "max" }, ]), booleanDescriptor("thinking", "Thinking"), ], }), }, { slug: "claude-haiku-4-5", name: "Claude Haiku 4.5", isCustom: false, capabilities: createModelCapabilities({ optionDescriptors: [ selectDescriptor("effort", "Reasoning", [ { id: "low", label: "low" }, { id: "medium", label: "medium", isDefault: true }, { id: "high", label: "high" }, ]), booleanDescriptor("thinking", "Thinking"), ], }), }, ], }, ]; const CODEX_INSTANCE_ID = ProviderInstanceId.make("codex"); const CLAUDE_INSTANCE_ID = ProviderInstanceId.make("claudeAgent"); const OPENCODE_INSTANCE_ID = ProviderInstanceId.make("opencode"); function buildCodexProvider(models: ServerProvider["models"]): ServerProvider { return { driver: ProviderDriverKind.make("codex"), instanceId: ProviderInstanceId.make("codex"), displayName: "Codex", enabled: true, installed: true, version: "0.116.0", status: "ready", auth: { status: "authenticated" }, checkedAt: new Date().toISOString(), models, slashCommands: [], skills: [], }; } function buildOpenCodeProvider(models: ServerProvider["models"]): ServerProvider { return { driver: ProviderDriverKind.make("opencode"), instanceId: ProviderInstanceId.make("opencode"), enabled: true, installed: true, version: "1.0.0", status: "ready", auth: { status: "authenticated" }, checkedAt: new Date().toISOString(), models, slashCommands: [], skills: [], }; } async function mountPicker(props: { activeInstanceId?: ProviderInstanceId; model: string; lockedProvider: ProviderDriverKind | null; lockedContinuationGroupKey?: string | null; providers?: ReadonlyArray<ServerProvider>; settings?: UnifiedSettings; triggerVariant?: "ghost" | "outline"; }) { const host = document.createElement("div"); document.body.append(host); const onInstanceModelChange = vi.fn(); const providers = props.providers ?? TEST_PROVIDERS; const instanceEntries = sortProviderInstanceEntries(deriveProviderInstanceEntries(providers)); const activeInstanceId = props.activeInstanceId ?? CODEX_INSTANCE_ID; const modelOptionsByInstance = getCustomModelOptionsByInstance( props.settings ?? DEFAULT_UNIFIED_SETTINGS, providers, activeInstanceId, props.model, ); const screen = await render( <ProviderModelPicker activeInstanceId={activeInstanceId} model={props.model} lockedProvider={props.lockedProvider} lockedContinuationGroupKey={props.lockedContinuationGroupKey ?? null} instanceEntries={instanceEntries} modelOptionsByInstance={modelOptionsByInstance} triggerVariant={props.triggerVariant} onInstanceModelChange={onInstanceModelChange} />, { container: host }, ); return { onInstanceModelChange, // Back-compat alias used by callers that still assert on the old callback // name. Delegates to the instance-aware mock so existing expectations work. get onProviderModelChange() { return onInstanceModelChange; }, cleanup: async () => { await screen.unmount(); host.remove(); }, }; } function getModelPickerListElement() { const modelPickerList = document.querySelector<HTMLElement>(".model-picker-list"); expect(modelPickerList).not.toBeNull(); return modelPickerList!; } function getModelPickerListText() { return getModelPickerListElement().textContent ?? ""; } function getVisibleModelNames() { return Array.from(getModelPickerListElement().querySelectorAll<HTMLDivElement>("div.font-medium")) .map((element) => element.textContent?.replace(/New$/u, "").trim() ?? "") .filter((text) => text.length > 0); } function getSidebarProviderOrder() { return Array.from(document.querySelectorAll<HTMLElement>("[data-model-picker-provider]")).map( (element) => element.dataset.modelPickerProvider ?? "", ); } describe("ProviderModelPicker", () => { beforeEach(async () => { // Reset test environment before each test await __resetLocalApiForTests(); }); afterEach(async () => { document.body.innerHTML = ""; await __resetLocalApiForTests(); }); it("shows provider sidebar in unlocked mode", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, }); try { await page.getByRole("button").click(); await vi.waitFor(() => { const text = document.body.textContent ?? ""; expect(text).not.toContain("Codex"); expect(text).toContain("Claude"); expect(text).toContain("Claude Opus 4.6"); }); } finally { await mounted.cleanup(); } }); it("shows favorites first in the provider sidebar", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, }); try { await page.getByRole("button").click(); await vi.waitFor(() => { expect(getSidebarProviderOrder().slice(0, 3)).toEqual([ "favorites", "codex", "claudeAgent", ]); }); } finally { await mounted.cleanup(); } }); it("filters models by selected provider in sidebar", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, }); try { await page.getByRole("button").click(); // Start with Claude models visible await vi.waitFor(() => { const text = document.body.textContent ?? ""; expect(text).not.toContain("GPT-5 Codex"); expect(text).toContain("Claude Opus 4.6"); }); // Click on Codex provider in sidebar await vi.waitFor(() => { expect(document.querySelector('[data-model-picker-provider="codex"]')).not.toBeNull(); }); await page.getByRole("button", { name: "Codex", exact: true }).click(); // Now should only show Codex models await vi.waitFor(() => { const listText = getModelPickerListText(); expect(listText).toContain("GPT-5 Codex"); expect(listText).not.toContain("Claude Opus 4.6"); }); } finally { await mounted.cleanup(); } }); it("uses client model visibility and ordering preferences", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, settings: { ...DEFAULT_UNIFIED_SETTINGS, providerModelPreferences: { [CLAUDE_INSTANCE_ID]: { hiddenModels: ["claude-opus-4-6"], modelOrder: ["claude-haiku-4-5", "claude-sonnet-4-6"], }, }, }, }); try { await page.getByRole("button").click(); await vi.waitFor(() => { expect(getVisibleModelNames()).toEqual(["Claude Haiku 4.5", "Claude Sonnet 4.6"]); expect(getModelPickerListText()).not.toContain("Claude Opus 4.6"); }); } finally { await mounted.cleanup(); } }); it("focuses the search input after selecting a sidebar provider", async () => { const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: null, }); try { await page.getByRole("button").click(); await vi.waitFor(() => { expect(document.querySelector('[data-model-picker-provider="codex"]')).not.toBeNull(); }); await page.getByRole("button", { name: "Codex", exact: true }).click(); await vi.waitFor(() => { const searchInput = document.querySelector<HTMLInputElement>( 'input[placeholder="Search models..."]', ); expect(searchInput).not.toBeNull(); expect(document.activeElement).toBe(searchInput); }); } finally { await mounted.cleanup(); } }); it("shows locked provider header and only its models in locked mode", async () => { localStorage.setItem( "t3code:client-settings:v1", JSON.stringify({ ...DEFAULT_CLIENT_SETTINGS, favorites: [ { provider: "codex", model: "gpt-5-codex" }, { provider: "claudeAgent", model: "claude-sonnet-4-6" }, ], }), ); const mounted = await mountPicker({ activeInstanceId: CLAUDE_INSTANCE_ID, model: "claude-opus-4-6", lockedProvider: ProviderDriverKind.make("claudeAgent"), }); try { await page.getByRole("button").click(); await vi.waitFor(() => { const text = document.body.textContent ?? ""; // Should show locked provider label expect(text).toContain("Claude"); expect(getVisibleModelNames()).toEqual([ "Claude Sonnet 4.6", "Claude Opus 4.6", "Claude Haiku 4.5", ]); }); } finally { localStorage.removeItem("t3code:client-settings:v1"); await mounted.cleanup(); } });