Files
OmniRoute/tests/unit/playground-model-selection-3731.test.ts
Diego Rodrigues de Sa e Souza e8fce67e70 feat(dashboard): add search to Playground model picker dropdown (#4086) (#6811)
* feat(dashboard): add search to Playground model picker dropdown (#4086)

The shared ModelSelectModal (combo builder + CLI-code cards) already had
search, but the Playground's raw model <select> in StudioConfigPane stayed
a flat unsearchable list - unusable once a provider like OpenRouter
contributed 50+ models.

Adds a search input above the dropdown that filters options via
filterModelsByQuery() (Turkish-safe accent/case-insensitive match, reusing
matchesSearch()). The currently selected model always stays pinned in the
list even when it doesn't match the query, so typing never silently swaps
the active selection. Reuses the existing common.search i18n key already
translated in all 42 locales - no new key needed.

* chore(6811): re-sync onto release tip; CHANGELOG → changelog.d fragment (fragments-first)
2026-07-10 19:40:13 -03:00

75 lines
3.3 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import {
pickDefaultModel,
resolveModelFilterKey,
filterModelsByQuery,
} from "../../src/app/(dashboard)/dashboard/playground/components/modelSelection.ts";
// Regression guards for #3731 (dup #3009): the Playground model selector was unusable
// for custom OpenAI-compatible providers.
test("resolveModelFilterKey: built-in provider filters by its id", () => {
assert.equal(resolveModelFilterKey("openai", undefined, false), "openai");
assert.equal(resolveModelFilterKey("anthropic", undefined, false), "anthropic");
});
test("resolveModelFilterKey: compatible provider WITH a resolved prefix filters by the prefix", () => {
assert.equal(resolveModelFilterKey("openai-compatible-abc123", "myco", true), "myco");
});
test("resolveModelFilterKey: compatible provider WITHOUT a prefix shows the full catalog (#3731)", () => {
// Defect A: the old logic returned the raw connection id, which matches nothing in the
// catalog and emptied the selector. It must return undefined (full catalog) instead.
const key = resolveModelFilterKey("openai-compatible-abc123", undefined, true);
assert.equal(key, undefined);
assert.notEqual(key, "openai-compatible-abc123");
});
test("resolveModelFilterKey: empty provider yields undefined", () => {
assert.equal(resolveModelFilterKey("", undefined, false), undefined);
});
test("pickDefaultModel: empty list selects nothing", () => {
assert.equal(pickDefaultModel("", []), null);
assert.equal(pickDefaultModel("gpt-4o", []), null);
});
test("pickDefaultModel: empty current model auto-selects the first model (#3731)", () => {
// Defect B: provider change reset the model to "" and nothing picked a default, so the
// chat failed with "Set a model". The first available model must be auto-selected.
assert.equal(pickDefaultModel("", ["myco/gpt-4o", "myco/gpt-4o-mini"]), "myco/gpt-4o");
assert.equal(pickDefaultModel(undefined, ["a", "b"]), "a");
});
test("pickDefaultModel: a current model not in the list is replaced by the first", () => {
assert.equal(pickDefaultModel("stale/model", ["a", "b"]), "a");
});
test("pickDefaultModel: a valid current model is kept (no redundant update)", () => {
assert.equal(pickDefaultModel("b", ["a", "b"]), null);
});
// Regression guards for #4086: search/filter on the raw Playground model <select>.
test("filterModelsByQuery: empty query returns the full list unchanged", () => {
const models = ["openai/gpt-4o", "anthropic/claude-3"];
assert.deepEqual(filterModelsByQuery(models, ""), models);
assert.deepEqual(filterModelsByQuery(models, " "), models);
});
test("filterModelsByQuery: matches case-insensitively on substring", () => {
const models = ["openai/gpt-4o", "anthropic/claude-3", "openrouter/mistral-large"];
assert.deepEqual(filterModelsByQuery(models, "GPT"), ["openai/gpt-4o"]);
assert.deepEqual(filterModelsByQuery(models, "claude"), ["anthropic/claude-3"]);
});
test("filterModelsByQuery: matches provider/namespace prefix", () => {
const models = ["openai/gpt-4o", "anthropic/claude-3", "openrouter/mistral-large"];
assert.deepEqual(filterModelsByQuery(models, "openrouter"), ["openrouter/mistral-large"]);
});
test("filterModelsByQuery: no matches returns an empty list", () => {
assert.deepEqual(filterModelsByQuery(["a", "b"], "zzz"), []);
});