mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 17:30:31 +00:00
150 lines
5.2 KiB
TypeScript
150 lines
5.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { createAppServerDiagnostics } from "../../../src/app-server/compatibility";
|
|
import { createChatState } from "../../../src/features/chat/chat-state";
|
|
import {
|
|
effortStatusLines,
|
|
runtimeToolbarChoices,
|
|
modelStatusLines,
|
|
runtimeSnapshotForChatState,
|
|
statusSummaryLines,
|
|
toolbarViewModel,
|
|
} from "../../../src/features/chat/view-model";
|
|
import type { Model } from "../../../src/generated/app-server/v2/Model";
|
|
import type { Thread } from "../../../src/generated/app-server/v2/Thread";
|
|
import type { ConfigReadResponse } from "../../../src/generated/app-server/v2/ConfigReadResponse";
|
|
|
|
describe("chat view model", () => {
|
|
it("builds toolbar rows from immutable chat state snapshots", () => {
|
|
const state = createChatState();
|
|
state.activeThreadId = "thread-1";
|
|
state.listedThreads = [threadFixture("thread-1", "Active"), threadFixture("thread-2", "Other")];
|
|
state.turnLifecycle = { kind: "running", turnId: "turn" };
|
|
state.openDetails = new Set(["history"]);
|
|
state.effectiveConfig = effectiveConfigFixture({ model: "gpt-5.5", model_reasoning_effort: "high" });
|
|
state.appServerDiagnostics = createAppServerDiagnostics();
|
|
|
|
const model = toolbarViewModel({
|
|
state,
|
|
snapshot: runtimeSnapshotForChatState({ state }),
|
|
connected: true,
|
|
turnBusy: true,
|
|
vaultPath: "/vault",
|
|
configuredCommand: "codex",
|
|
archiveConfirmThreadId: "thread-2",
|
|
archiveExportEnabled: true,
|
|
modelChoices: [{ label: "gpt-5.5", selected: true, onClick: () => undefined }],
|
|
effortChoices: [{ label: "high", selected: true, onClick: () => undefined }],
|
|
renameState: (threadId) => (threadId === "thread-1" ? { draft: "Active", generating: false } : null),
|
|
});
|
|
|
|
expect(model.statusState).toBe("running");
|
|
expect(model.openPanel).toBe("history");
|
|
expect(model.threads).toMatchObject([
|
|
{ threadId: "thread-1", title: "Active", selected: true, disabled: false, rename: { draft: "Active" } },
|
|
{ threadId: "thread-2", title: "Other", selected: false, disabled: true, archiveConfirm: { active: true } },
|
|
]);
|
|
expect(model.modelChoices).toHaveLength(1);
|
|
});
|
|
|
|
it("builds slash-command status lines from chat state", () => {
|
|
const state = createChatState();
|
|
state.activeThreadId = "thread-1";
|
|
state.effectiveConfig = effectiveConfigFixture({
|
|
model: "gpt-5.5",
|
|
model_provider: "openai",
|
|
model_reasoning_effort: "high",
|
|
service_tier: "fast",
|
|
});
|
|
state.availableModels = [modelFixture("gpt-5.5")];
|
|
const snapshot = runtimeSnapshotForChatState({ state });
|
|
|
|
expect(statusSummaryLines(state, snapshot)[1]).toBe("Thread: thread-1");
|
|
expect(modelStatusLines(state, snapshot, "Default")).toContain("Model: gpt-5.5");
|
|
expect(modelStatusLines(state, snapshot, "Default")).toContain("Mode: Default");
|
|
expect(effortStatusLines(state, snapshot)).toContain("Supported: high");
|
|
});
|
|
|
|
it("builds runtime toolbar choices from immutable chat state snapshots", () => {
|
|
const state = createChatState();
|
|
state.effectiveConfig = effectiveConfigFixture({ model: "gpt-5.5", model_reasoning_effort: "high" });
|
|
state.availableModels = [modelFixture("gpt-5.5"), modelFixture("gpt-5-mini")];
|
|
const selectedModels: (string | null)[] = [];
|
|
const selectedEfforts: string[] = [];
|
|
|
|
const choices = runtimeToolbarChoices({
|
|
state,
|
|
snapshot: runtimeSnapshotForChatState({ state }),
|
|
setRequestedModel: (model) => {
|
|
selectedModels.push(model);
|
|
},
|
|
setRequestedReasoningEffort: (effort) => {
|
|
if (effort) selectedEfforts.push(effort);
|
|
},
|
|
});
|
|
|
|
expect(choices.modelChoices).toMatchObject([
|
|
{ label: "gpt-5-mini", selected: false },
|
|
{ label: "gpt-5.5", selected: true },
|
|
]);
|
|
expect(choices.effortChoices).toMatchObject([{ label: "high", selected: true }]);
|
|
|
|
choices.modelChoices[0]?.onClick();
|
|
choices.effortChoices[0]?.onClick();
|
|
expect(selectedModels).toEqual(["gpt-5-mini"]);
|
|
expect(selectedEfforts).toEqual(["high"]);
|
|
});
|
|
});
|
|
|
|
function effectiveConfigFixture(config: Record<string, unknown>): ConfigReadResponse {
|
|
return {
|
|
config: config as ConfigReadResponse["config"],
|
|
origins: {},
|
|
layers: null,
|
|
};
|
|
}
|
|
|
|
function threadFixture(id: string, name: string): Thread {
|
|
return {
|
|
id,
|
|
sessionId: "session",
|
|
forkedFromId: null,
|
|
preview: "",
|
|
ephemeral: false,
|
|
modelProvider: "openai",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
status: { type: "idle" },
|
|
path: null,
|
|
cwd: "/vault",
|
|
cliVersion: "0.0.0",
|
|
source: "appServer",
|
|
threadSource: null,
|
|
agentNickname: null,
|
|
agentRole: null,
|
|
gitInfo: null,
|
|
name,
|
|
turns: [],
|
|
};
|
|
}
|
|
|
|
function modelFixture(model: string): Model {
|
|
return {
|
|
id: model,
|
|
model,
|
|
upgrade: null,
|
|
upgradeInfo: null,
|
|
availabilityNux: null,
|
|
displayName: model,
|
|
description: "",
|
|
hidden: false,
|
|
supportedReasoningEfforts: [{ reasoningEffort: "high", description: "" }],
|
|
defaultReasoningEffort: "high",
|
|
inputModalities: [],
|
|
supportsPersonality: false,
|
|
additionalSpeedTiers: [],
|
|
serviceTiers: [],
|
|
defaultServiceTier: null,
|
|
isDefault: true,
|
|
};
|
|
}
|