footer-keymap.test.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /** @jsxImportSource @opentui/solid */
  2. import { createDefaultOpenTuiKeymap } from "@opentui/keymap/opentui"
  3. import { testRender, useRenderer } from "@opentui/solid"
  4. import { OpencodeKeymapProvider, registerOpencodeKeymap } from "@opencode-ai/tui/keymap"
  5. import { resolve } from "@opencode-ai/tui/config/v1"
  6. import { expect, test } from "bun:test"
  7. import { createComponent, createSignal } from "solid-js"
  8. import { RunFooterView } from "../src/mini/footer.view"
  9. import { RUN_THEME_FALLBACK } from "../src/mini/theme"
  10. import type { FooterState, FooterSubagentState, FooterView } from "../src/mini/types"
  11. test("down opens subagents from an empty prompt", async () => {
  12. const [state] = createSignal<FooterState>({
  13. phase: "idle",
  14. status: "",
  15. queue: 0,
  16. model: "gpt-5",
  17. duration: "",
  18. usage: "",
  19. first: false,
  20. interrupt: 0,
  21. exit: 0,
  22. })
  23. const [view] = createSignal<FooterView>({ type: "prompt" })
  24. const [subagents] = createSignal<FooterSubagentState>({
  25. tabs: [
  26. {
  27. sessionID: "subagent-1",
  28. partID: "part-1",
  29. callID: "call-1",
  30. label: "Explore",
  31. description: "Inspect the keymap",
  32. status: "running",
  33. lastUpdatedAt: 1,
  34. },
  35. ],
  36. details: {},
  37. permissions: [],
  38. questions: [],
  39. })
  40. const config = resolve(
  41. { keybinds: { editor_open: "none", session_queued_prompts: "none" } },
  42. { terminalSuspend: true },
  43. )
  44. let offKeymap: (() => void) | undefined
  45. function Harness() {
  46. const renderer = useRenderer()
  47. const keymap = createDefaultOpenTuiKeymap(renderer)
  48. offKeymap = registerOpencodeKeymap(keymap, renderer, config)
  49. return createComponent(OpencodeKeymapProvider, {
  50. keymap,
  51. get children() {
  52. return (
  53. <RunFooterView
  54. directory="/tmp"
  55. findFiles={async () => []}
  56. agents={() => []}
  57. references={() => []}
  58. commands={() => []}
  59. providers={() => undefined}
  60. currentModel={() => undefined}
  61. variants={() => []}
  62. currentVariant={() => undefined}
  63. state={state}
  64. view={view}
  65. subagent={subagents}
  66. theme={() => RUN_THEME_FALLBACK}
  67. tuiConfig={config}
  68. agent="opencode"
  69. onSubmit={() => true}
  70. onPermissionReply={() => {}}
  71. onQuestionReply={() => {}}
  72. onQuestionReject={() => {}}
  73. onCycle={() => {}}
  74. onInterrupt={() => false}
  75. onEditorOpen={async () => undefined}
  76. onInputClear={() => {}}
  77. onExit={() => {}}
  78. onModelSelect={() => {}}
  79. onVariantSelect={() => {}}
  80. onRows={() => {}}
  81. onLayout={() => {}}
  82. onStatus={() => {}}
  83. onQueuedRemove={async () => true}
  84. />
  85. )
  86. },
  87. })
  88. }
  89. const app = await testRender(() => <Harness />, { width: 100, height: 8, kittyKeyboard: true })
  90. try {
  91. await app.renderOnce()
  92. expect(app.renderer.currentFocusedEditor?.plainText).toBe("")
  93. app.mockInput.pressArrow("down")
  94. await app.renderOnce()
  95. expect(app.captureCharFrame()).toContain("Select subagent")
  96. } finally {
  97. app.renderer.currentFocusedRenderable?.blur()
  98. app.renderer.currentFocusedEditor?.blur()
  99. offKeymap?.()
  100. app.renderer.destroy()
  101. }
  102. })