prompt.shared.test.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import { describe, expect, test } from "bun:test"
  2. import {
  3. createPromptHistory,
  4. isCompactCommand,
  5. isExitCommand,
  6. isNewCommand,
  7. movePromptHistory,
  8. pushPromptHistory,
  9. } from "../../src/mini/prompt.shared"
  10. import type { RunPrompt } from "../../src/mini/types"
  11. function prompt(text: string, parts: RunPrompt["parts"] = []): RunPrompt {
  12. return { text, parts }
  13. }
  14. describe("run prompt shared", () => {
  15. test("filters blank prompts and dedupes consecutive history", () => {
  16. const out = createPromptHistory([prompt(" "), prompt("one"), prompt("one"), prompt("two"), prompt("one")])
  17. expect(out.items.map((item) => item.text)).toEqual(["one", "two", "one"])
  18. expect(out.index).toBeNull()
  19. expect(out.draft).toBe("")
  20. })
  21. test("push ignores blanks and dedupes only the latest item", () => {
  22. const base = createPromptHistory([prompt("one")])
  23. expect(pushPromptHistory(base, prompt(" ")).items.map((item) => item.text)).toEqual(["one"])
  24. expect(pushPromptHistory(base, prompt("one")).items.map((item) => item.text)).toEqual(["one"])
  25. expect(pushPromptHistory(base, prompt("two")).items.map((item) => item.text)).toEqual(["one", "two"])
  26. })
  27. test("moves through history only at input boundaries and restores draft", () => {
  28. const base = createPromptHistory([prompt("one"), prompt("two")])
  29. expect(movePromptHistory(base, -1, "draft", 1)).toEqual({
  30. state: base,
  31. apply: false,
  32. })
  33. const up = movePromptHistory(base, -1, "draft", 0)
  34. expect(up.apply).toBe(true)
  35. expect(up.text).toBe("two")
  36. expect(up.cursor).toBe(0)
  37. expect(up.state.index).toBe(1)
  38. expect(up.state.draft).toBe("draft")
  39. const older = movePromptHistory(up.state, -1, "two", 0)
  40. expect(older.apply).toBe(true)
  41. expect(older.text).toBe("one")
  42. expect(older.cursor).toBe(0)
  43. expect(older.state.index).toBe(0)
  44. const newer = movePromptHistory(older.state, 1, "one", 3)
  45. expect(newer.apply).toBe(true)
  46. expect(newer.text).toBe("two")
  47. expect(newer.cursor).toBe(3)
  48. expect(newer.state.index).toBe(1)
  49. const draft = movePromptHistory(newer.state, 1, "two", 3)
  50. expect(draft.apply).toBe(true)
  51. expect(draft.text).toBe("draft")
  52. expect(draft.cursor).toBe(5)
  53. expect(draft.state.index).toBeNull()
  54. })
  55. test("uses display-width cursors for history restoration", () => {
  56. const base = createPromptHistory([prompt("one"), prompt("中文")])
  57. const latest = movePromptHistory(base, -1, "草稿", 0)
  58. expect(latest.apply).toBe(true)
  59. expect(latest.text).toBe("中文")
  60. expect(latest.cursor).toBe(0)
  61. const older = movePromptHistory(latest.state, -1, "中文", 0)
  62. expect(older.apply).toBe(true)
  63. expect(older.text).toBe("one")
  64. expect(older.cursor).toBe(0)
  65. const newer = movePromptHistory(older.state, 1, "one", Bun.stringWidth("one"))
  66. expect(newer.apply).toBe(true)
  67. expect(newer.text).toBe("中文")
  68. expect(newer.cursor).toBe(Bun.stringWidth("中文"))
  69. const draft = movePromptHistory(newer.state, 1, "中文", Bun.stringWidth("中文"))
  70. expect(draft.apply).toBe(true)
  71. expect(draft.text).toBe("草稿")
  72. expect(draft.cursor).toBe(Bun.stringWidth("草稿"))
  73. })
  74. test("recognizes exit commands", () => {
  75. expect(isExitCommand("/exit")).toBe(true)
  76. expect(isExitCommand(" /Quit ")).toBe(true)
  77. expect(isExitCommand("/quit now")).toBe(false)
  78. })
  79. test("recognizes the new-session command", () => {
  80. expect(isNewCommand("/new")).toBe(true)
  81. expect(isNewCommand(" /NEW ")).toBe(true)
  82. expect(isNewCommand("/new now")).toBe(false)
  83. })
  84. test("recognizes only the compact command", () => {
  85. expect(isCompactCommand("/compact")).toBe(true)
  86. expect(isCompactCommand(" /COMPACT ")).toBe(true)
  87. expect(isCompactCommand("/summarize")).toBe(false)
  88. })
  89. })