Skip to content

Commit 7028ee4

Browse files
committed
test(tui): add regression test for skill picker onSelect preserving prompt text
Added a regression test at packages/opencode/test/cli/tui/prompt-skill-select.test.ts that exercises the skill picker onSelect handler: - 'inserts skill name without wiping existing prompt text' — sets up an input with pre-existing text, simulates onSelect with a skill name, asserts the result is 'some existing text/test-skill ' (append via insertText rather than replacement via setText) - 'inserts skill name into empty prompt' — same assertion on an empty initial text Both pass with the current insertText-based logic and would fail with the old setText-based logic (which replaced the full input content). Addresses review comment: anomalyco#27632 (comment)
1 parent 3c1a384 commit 7028ee4

1 file changed

Lines changed: 80 additions & 0 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { describe, expect, test } from "bun:test"
2+
3+
// Regression test for the DialogSkill onSelect handler in
4+
// packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx.
5+
//
6+
// Before the fix, onSelect called `input.setText(`/${skill} `)` which
7+
// replaced the entire prompt content, wiping any text the user had already
8+
// typed. After the fix, it calls `input.insertText(`/${skill} `)` which
9+
// inserts at the cursor position, preserving existing text.
10+
//
11+
// `handleSkillSelect` below has the exact shape of the production handler after
12+
// the fix.
13+
14+
type TextInput = {
15+
plainText: string
16+
insertText(text: string): void
17+
}
18+
19+
type Store = {
20+
prompt: { input: string; parts: unknown[] }
21+
extmarkToPartIndex: Map<number, number>
22+
}
23+
24+
25+
function handleSkillSelect(input: TextInput, store: Store, syncExtmarks: () => void, skill: string) {
26+
input.insertText(`/${skill} `)
27+
store.prompt.input = input.plainText
28+
syncExtmarks()
29+
}
30+
31+
describe("Prompt skill picker onSelect", () => {
32+
test("inserts skill name without wiping existing prompt text", () => {
33+
let text = "some existing text"
34+
const input: TextInput = {
35+
get plainText() {
36+
return text
37+
},
38+
insertText(insert: string) {
39+
text = text + insert
40+
},
41+
}
42+
const store: Store = {
43+
prompt: { input: "some existing text", parts: [] },
44+
extmarkToPartIndex: new Map(),
45+
}
46+
let synced = false
47+
48+
handleSkillSelect(input, store, () => { synced = true }, "test-skill")
49+
50+
expect(input.plainText).toBe("some existing text/test-skill ")
51+
expect(store.prompt.input).toBe("some existing text/test-skill ")
52+
expect(store.prompt.parts).toEqual([])
53+
expect(synced).toBe(true)
54+
})
55+
56+
test("inserts skill name into empty prompt", () => {
57+
let text = ""
58+
const input: TextInput = {
59+
get plainText() {
60+
return text
61+
},
62+
insertText(insert: string) {
63+
text = text + insert
64+
},
65+
}
66+
const store: Store = {
67+
prompt: { input: "", parts: [] },
68+
extmarkToPartIndex: new Map(),
69+
}
70+
let synced = false
71+
72+
handleSkillSelect(input, store, () => { synced = true }, "my-skill")
73+
74+
expect(input.plainText).toBe("/my-skill ")
75+
expect(store.prompt.input).toBe("/my-skill ")
76+
expect(synced).toBe(true)
77+
})
78+
})
79+
80+

0 commit comments

Comments
 (0)