prompt-persistence.test.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. import { beforeAll, describe, expect, mock, test } from "bun:test"
  2. import type { AsyncStorage } from "@solid-primitives/storage"
  3. import { createEffect, createRoot } from "solid-js"
  4. import { ServerScope } from "@/utils/server-scope"
  5. let Prompt: typeof import("@/context/prompt")
  6. let read: ((value: string | null) => void) | undefined
  7. const storage: AsyncStorage = {
  8. getItem: () => new Promise((resolve) => (read = resolve)),
  9. setItem: async () => undefined,
  10. removeItem: async () => undefined,
  11. clear: async () => undefined,
  12. key: async () => null,
  13. getLength: async () => 0,
  14. length: Promise.resolve(0),
  15. }
  16. beforeAll(async () => {
  17. mock.module("@solidjs/router", () => ({
  18. useParams: () => ({}),
  19. useSearchParams: () => [{}],
  20. useLocation: () => ({ pathname: "", query: {} }),
  21. useNavigate: () => () => undefined,
  22. }))
  23. mock.module("@opencode-ai/ui/context", () => ({
  24. createSimpleContext: () => ({
  25. use: () => undefined,
  26. provider: () => undefined,
  27. }),
  28. }))
  29. mock.module("@/context/platform", () => ({
  30. usePlatform: () => ({ platform: "desktop", storage: () => storage }),
  31. }))
  32. Prompt = await import("@/context/prompt")
  33. })
  34. describe("prompt persistence", () => {
  35. test("waits for an async draft to hydrate before reporting ready", async () => {
  36. await new Promise<void>((resolve, reject) => {
  37. createRoot((dispose) => {
  38. const session = Prompt.createPromptSession(ServerScope.local, { draftID: "draft-async" })
  39. const ready = Prompt.createPromptReady(() => session)
  40. expect(ready()).toBe(false)
  41. expect(session.current()[0]).toMatchObject({ type: "text", content: "" })
  42. read?.(
  43. JSON.stringify({
  44. prompt: [{ type: "text", content: "persisted draft", start: 0, end: 15 }],
  45. cursor: 15,
  46. context: { items: [] },
  47. }),
  48. )
  49. createEffect(() => {
  50. if (!ready()) return
  51. try {
  52. expect(session.current()[0]).toMatchObject({ type: "text", content: "persisted draft" })
  53. dispose()
  54. resolve()
  55. } catch (error) {
  56. dispose()
  57. reject(error)
  58. }
  59. })
  60. })
  61. })
  62. })
  63. })