Files
cline/apps/cli/src/tui/views/onboarding/model.test.ts
T
Saoud Rizwan a1d5589d19 feat: allow selecting Cline free models on the ClinePass provider (#12140)
* feat(llms): include Cline free models in the cline-pass catalog

* feat(vscode): show Subscribed/Free model tabs on the ClinePass provider

* feat(cli): show Subscribed/Free sections in the ClinePass model picker

* fix(cli): drop redundant browse-all entry from ClinePass picker

* fix(cli): show only subscribed models in ClinePass onboarding picker

* feat(cli): include free models and quota explainer in ClinePass onboarding picker

* fix: shorten ClinePass free section copy

* fix(cli): strip redundant free markers from sectioned picker names

* fix: drop free from ClinePass free section copy

* fix: tighten ClinePass free section copy

* refactor: address review feedback on ClinePass free models

- single buildFeaturedModelEntries(providerId) dispatcher, builders private
- rename isClineProvider to isClineManagedProvider (includes cline-pass)
- use isClineManagedProvider in the free-model cost check
- themed tab border, pretty names on free model cards
- clearer cline-pass cost test name

* fix: address ClinePass free-model review blockers

- Stop re-sorting the cline-pass live catalog by release date in
  mergeKnownModels: free models carry OpenRouter release dates, so the
  sort could put a free model first and make it the fallback default
  when the bundled default id rotates out of the live clinePass bucket.
  Preserve the normalize-time order (pass models first) and pin it with
  an end-to-end resolveProviderConfig test.
- Add the browse-all escape to the CLI ClinePass picker when the
  clinePass bucket is empty (bundled fallback after a fetch failure),
  so a subscriber isn't left with a free-models-only picker.
- Rename ErrorRow's local isClineManagedProvider to
  isClineUsageBillingProvider: it only matches the cline provider,
  unlike the shared util of the same name that also matches cline-pass.
2026-07-08 15:08:27 -07:00

144 lines
3.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
getMainMenuOptions,
getOAuthProviderLabel,
shouldUseFeaturedClineModelPicker,
toModelEntriesFromKnownModels,
toModelEntry,
toProviderEntry,
} from "./model";
describe("onboarding model helpers", () => {
it("hides ClinePass from the main menu unless its feature flag is enabled", () => {
expect(
getMainMenuOptions().some((option) => option.value === "cline-pass"),
).toBe(false);
expect(
getMainMenuOptions({ isClinePassEnabled: false }).some(
(option) => option.value === "cline-pass",
),
).toBe(false);
expect(
getMainMenuOptions({ isClinePassEnabled: true }).some(
(option) => option.value === "cline-pass",
),
).toBe(true);
});
it("maps provider catalog entries into onboarding provider entries", () => {
expect(
toProviderEntry({
id: "cline",
name: "Cline",
apiKey: "",
oauthAccessTokenPresent: true,
models: 12,
defaultModelId: "openai/gpt-5.3-codex",
}),
).toEqual({
id: "cline",
name: "Cline",
isOAuth: true,
isLocalAuth: false,
hasAuth: true,
models: 12,
defaultModelId: "openai/gpt-5.3-codex",
});
});
it("treats API key providers as authenticated when an API key exists", () => {
expect(
toProviderEntry({
id: "anthropic",
name: "Anthropic",
apiKey: "sk-test",
models: null,
}),
).toMatchObject({
id: "anthropic",
isOAuth: false,
isLocalAuth: false,
hasAuth: true,
models: null,
});
});
it("marks the OpenAI Codex CLI provider as local auth", () => {
expect(
toProviderEntry({
id: "openai-codex-cli",
name: "OpenAI Codex CLI",
models: null,
}),
).toMatchObject({
id: "openai-codex-cli",
isOAuth: false,
isLocalAuth: true,
});
});
it("maps model names and reasoning support strictly", () => {
expect(
toModelEntry({
id: "anthropic/claude-sonnet-4.6",
supportsReasoning: false,
}),
).toEqual({
id: "anthropic/claude-sonnet-4.6",
name: "anthropic/claude-sonnet-4.6",
supportsReasoning: false,
});
expect(
toModelEntry({
id: "openai/gpt-5.3-codex",
name: "GPT-5.3 Codex",
supportsReasoning: true,
}),
).toEqual({
id: "openai/gpt-5.3-codex",
name: "GPT-5.3 Codex",
supportsReasoning: true,
});
});
it("maps resolved known models into sorted onboarding model entries", () => {
expect(
toModelEntriesFromKnownModels({
"gpt-5.2": {
name: "GPT-5.2",
capabilities: ["tools"],
},
"gpt-5.3-codex": {
name: "GPT-5.3 Codex",
capabilities: ["tools", "reasoning"],
},
}),
).toEqual([
{
id: "gpt-5.2",
name: "GPT-5.2",
supportsReasoning: false,
},
{
id: "gpt-5.3-codex",
name: "GPT-5.3 Codex",
supportsReasoning: true,
},
]);
});
it("formats OAuth provider labels for onboarding status views", () => {
expect(getOAuthProviderLabel("cline")).toBe("Cline");
expect(getOAuthProviderLabel("cline-pass")).toBe("ClinePass");
expect(getOAuthProviderLabel("openai-codex")).toBe("ChatGPT");
expect(getOAuthProviderLabel("oca")).toBe("oca");
});
it("uses the featured Cline model picker for the Cline and ClinePass providers", () => {
expect(shouldUseFeaturedClineModelPicker("cline")).toBe(true);
expect(shouldUseFeaturedClineModelPicker("cline-pass")).toBe(true);
expect(shouldUseFeaturedClineModelPicker("anthropic")).toBe(false);
});
});