| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- import { afterEach, describe, expect, mock, spyOn, test } from "bun:test"
- import { OpenCode } from "@opencode-ai/client/promise"
- import type { Resolved } from "../../src/config"
- import { resolveMiniSettings, resolveModelInfo, resolveRunTuiConfig } from "../../src/mini/runtime.boot"
- import { catalogModel, catalogProvider } from "./fixture/catalog"
- import { createTuiResolvedConfig } from "../fixture/tui-runtime"
- function config(input?: {
- leader?: string
- leaderTimeout?: number
- bindings?: Partial<{
- commandList: string[]
- variantCycle: string[]
- interrupt: string[]
- historyPrevious: string[]
- historyNext: string[]
- inputClear: string[]
- inputSubmit: string[]
- inputNewline: string[]
- }>
- }): Resolved {
- const bind = input?.bindings
- return createTuiResolvedConfig({
- leader: input?.leaderTimeout === undefined ? undefined : { timeout: input.leaderTimeout },
- keybinds: {
- ...(input?.leader && { leader: input.leader }),
- ...(bind?.commandList && { command_list: bind.commandList }),
- ...(bind?.variantCycle && { variant_cycle: bind.variantCycle }),
- ...(bind?.interrupt && { session_interrupt: bind.interrupt }),
- ...(bind?.historyPrevious && { history_previous: bind.historyPrevious }),
- ...(bind?.historyNext && { history_next: bind.historyNext }),
- ...(bind?.inputClear && { input_clear: bind.inputClear }),
- ...(bind?.inputSubmit && { input_submit: bind.inputSubmit }),
- ...(bind?.inputNewline && { input_newline: bind.inputNewline }),
- },
- })
- }
- describe("run runtime boot", () => {
- afterEach(() => {
- mock.restore()
- })
- test("reads footer keybinds from resolved keybind config", async () => {
- const input = config({
- leader: "ctrl+g",
- bindings: {
- commandList: ["ctrl+p"],
- variantCycle: ["ctrl+t", "alt+t"],
- interrupt: ["ctrl+c"],
- historyPrevious: ["k"],
- historyNext: ["j"],
- inputClear: ["ctrl+l"],
- inputSubmit: ["ctrl+s"],
- inputNewline: ["alt+return"],
- },
- })
- const result = await resolveRunTuiConfig(input)
- expect(result.keybinds.get("leader")?.[0]?.key).toBe("ctrl+g")
- expect(result.leader.timeout).toBe(2000)
- expect(result.keybinds.get("command.palette.show")?.[0]?.key).toBe("ctrl+p")
- expect(result.keybinds.get("variant.cycle").map((item) => item.key)).toEqual(["ctrl+t", "alt+t"])
- expect(result.keybinds.get("session.interrupt")?.[0]?.key).toBe("ctrl+c")
- expect(result.keybinds.get("prompt.history.previous")?.[0]?.key).toBe("k")
- expect(result.keybinds.get("prompt.history.next")?.[0]?.key).toBe("j")
- expect(result.keybinds.get("prompt.clear")?.[0]?.key).toBe("ctrl+l")
- expect(result.keybinds.get("input.submit")?.[0]?.key).toBe("ctrl+s")
- expect(result.keybinds.get("input.newline")?.[0]?.key).toBe("alt+return")
- })
- test("falls back to default tui keymap config when config load fails", async () => {
- const result = await resolveRunTuiConfig(Promise.reject(new Error("boom")))
- expect(result.keybinds.get("leader")?.[0]?.key).toBe("ctrl+x")
- expect(result.leader.timeout).toBe(2000)
- expect(result.keybinds.get("command.palette.show")?.[0]?.key).toBe("ctrl+p")
- expect(result.keybinds.get("variant.cycle")?.[0]?.key).toBe("ctrl+t")
- expect(result.keybinds.get("session.interrupt")?.[0]?.key).toBe("escape")
- expect(result.keybinds.get("prompt.history.previous")?.[0]?.key).toBe("up")
- expect(result.keybinds.get("prompt.history.next")?.[0]?.key).toBe("down")
- expect(result.keybinds.get("prompt.clear")?.[0]?.key).toBe("ctrl+c")
- expect(result.keybinds.get("input.submit")?.[0]?.key).toBe("return")
- expect(result.keybinds.get("input.newline")?.[0]?.key).toBe("shift+return,ctrl+return,ctrl+j")
- expect(result.keybinds.get("prompt.queue")?.[0]?.key).toBe("alt+return")
- })
- test("preserves disabled leader from resolved tui config", async () => {
- const result = await resolveRunTuiConfig(config({ leader: "none" }))
- expect(result.keybinds.get("leader")).toEqual([])
- })
- test("preserves shared config while resolving independent Mini defaults", async () => {
- const result = await resolveRunTuiConfig(
- createTuiResolvedConfig({
- theme: { mode: "light" },
- leader: { timeout: 450 },
- }),
- )
- expect(result.theme).toEqual({ mode: "light" })
- expect(result.leader.timeout).toBe(450)
- expect(resolveMiniSettings(result)).toEqual({
- thinking: "hide",
- shell_output: "hide",
- turn_summary: "show",
- footer: "show",
- splash: "show",
- mono: false,
- })
- expect(
- resolveMiniSettings({
- mini: {
- thinking: "show",
- shell_output: "show",
- turn_summary: "hide",
- footer: "hide",
- splash: "hide",
- mono: true,
- },
- }),
- ).toEqual({
- thinking: "show",
- shell_output: "show",
- turn_summary: "hide",
- footer: "hide",
- splash: "hide",
- mono: true,
- })
- })
- test("loads v2 providers and models for model selector data", async () => {
- const sdk = OpenCode.make({ baseUrl: "https://opencode.test" })
- const location = { directory: "/workspace", project: { id: "proj_1", directory: "/workspace" } }
- const providerList = spyOn(sdk.provider, "list").mockResolvedValue({
- location,
- data: [catalogProvider("openai", "OpenAI")],
- } as never)
- spyOn(sdk.model, "list").mockResolvedValue({
- location,
- data: [catalogModel({ id: "gpt-5", providerID: "openai", variants: ["high", "minimal"] })],
- } as never)
- await expect(resolveModelInfo(sdk, { directory: "/workspace" })).resolves.toEqual({
- providers: [
- {
- id: "openai",
- name: "OpenAI",
- models: {
- "gpt-5": {
- name: "gpt-5",
- cost: {
- input: 0,
- },
- limit: {
- context: 128_000,
- },
- status: "active",
- variants: {
- high: {},
- minimal: {},
- },
- },
- },
- },
- ],
- })
- expect(providerList).toHaveBeenCalledWith({
- location: {
- directory: "/workspace",
- },
- })
- })
- })
|