mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-15 12:41:37 +03:00
87 lines
3.7 KiB
TypeScript
87 lines
3.7 KiB
TypeScript
import { expect, test } from "bun:test"
|
|
import { createTestRenderer } from "@opentui/core/testing"
|
|
import { Effect, FileSystem } from "effect"
|
|
import { Global } from "@opencode/util/global"
|
|
import type { SessionMessageInfo } from "@opencode/client"
|
|
import { createEventStream, createFetch, directory, json } from "./fixture/tui-client"
|
|
import { tmpdir } from "./fixture/fixture"
|
|
|
|
// Regression: a thought group mounted by tail-first backfill (older rows revealed
|
|
// after scrolling to the top) must still toggle when its disclosure is clicked.
|
|
test("clicking a backfilled thought group toggles it", async () => {
|
|
await using state = await tmpdir()
|
|
const setup = await createTestRenderer({ width: 112, height: 34, kittyKeyboard: true })
|
|
await setup.renderer.setupTerminal()
|
|
setup.renderer.start()
|
|
const session = {
|
|
id: "ses_backfill",
|
|
title: "Backfill",
|
|
projectID: "proj_test",
|
|
location: { directory },
|
|
cost: 0,
|
|
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
time: { created: 0, updated: 0 },
|
|
}
|
|
const model = { providerID: "fixture", id: "fixture" }
|
|
const messages: SessionMessageInfo[] = Array.from({ length: 14 }, (_, i) => [
|
|
{
|
|
type: "user" as const,
|
|
id: `user-${i}`,
|
|
text: `Turn ${String(i).padStart(2, "0")} prompt`,
|
|
time: { created: i * 10 },
|
|
},
|
|
{
|
|
type: "assistant" as const,
|
|
id: `a-${i}`,
|
|
agent: "build",
|
|
model,
|
|
finish: "stop" as const,
|
|
time: { created: i * 10 + 1, completed: i * 10 + 3 },
|
|
content: [
|
|
{
|
|
type: "reasoning" as const,
|
|
text: `**Turn ${i}**\n\nA fixed thought.`,
|
|
time: { created: i * 10 + 1, completed: i * 10 + 2 },
|
|
},
|
|
{ type: "text" as const, text: `Turn ${String(i).padStart(2, "0")} answer` },
|
|
],
|
|
},
|
|
]).flat()
|
|
const calls = createFetch((url) => {
|
|
if (url.pathname === "/api/session") return json({ data: [session], cursor: {} })
|
|
if (url.pathname === `/api/session/${session.id}`) return json({ data: session })
|
|
if (url.pathname === `/api/session/${session.id}/message`) return json({ data: messages.toReversed(), cursor: {} })
|
|
if (url.pathname === `/api/session/${session.id}/inbox` || url.pathname === `/api/session/${session.id}/permission`)
|
|
return json({ data: [] })
|
|
}, createEventStream())
|
|
const server = Bun.serve({ port: 0, idleTimeout: 0, fetch: (request) => calls.fetch(request) })
|
|
const { run } = await import("../src/app")
|
|
const task = Effect.runPromise(
|
|
run({
|
|
app: { name: "test", version: "test", channel: "test" },
|
|
server: { endpoint: { url: server.url.toString() } },
|
|
config: { get: async () => ({ animations: false, tabs: { enabled: false } }), update: async () => ({}) },
|
|
packages: { prepare: async () => ({ directory: "" }) },
|
|
args: { sessionID: session.id },
|
|
terminalHandoff: async () => ({ renderer: setup.renderer, mode: "dark", complete: () => {} }),
|
|
log: () => {},
|
|
}).pipe(Effect.provide(Global.layerWith({ state: state.path })), Effect.provide(FileSystem.layerNoop({}))),
|
|
)
|
|
try {
|
|
await setup.waitForFrame((frame) => frame.includes("Turn 13 answer"))
|
|
setup.mockInput.pressKey("g", { ctrl: true })
|
|
await setup.waitForFrame((frame) => frame.includes("Turn 00 prompt"))
|
|
await setup.waitForVisualIdle({ quietFrames: 3 })
|
|
const lines = setup.captureCharFrame().split("\n")
|
|
const row = lines.findIndex((line) => line.includes("+ Thought: Turn 0"))
|
|
expect(row).toBeGreaterThan(0)
|
|
await setup.mockMouse.click(6, row)
|
|
await setup.waitForVisualIdle({ quietFrames: 3 })
|
|
expect(setup.captureCharFrame()).toContain("A fixed thought.")
|
|
} finally {
|
|
setup.renderer.destroy()
|
|
await task
|
|
await server.stop()
|
|
}
|
|
}, 20000)
|