use-event.test.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. /** @jsxImportSource @opentui/solid */
  2. import { describe, expect, test } from "bun:test"
  3. import { testRender } from "@opentui/solid"
  4. import type { Event, GlobalEvent } from "@kirincode-ai/sdk/v2"
  5. import { onMount } from "solid-js"
  6. import { ProjectProvider, useProject } from "../../../src/context/project"
  7. import { SDKProvider } from "../../../src/context/sdk"
  8. import { useEvent } from "../../../src/context/event"
  9. import { createEventSource, createFetch, directory } from "../../fixture/tui-sdk"
  10. import { TestTuiContexts } from "../../fixture/tui-environment"
  11. const projectID = "proj_test"
  12. async function wait(fn: () => boolean, timeout = 2000) {
  13. const start = Date.now()
  14. while (!fn()) {
  15. if (Date.now() - start > timeout) throw new Error("timed out waiting for condition")
  16. await Bun.sleep(10)
  17. }
  18. }
  19. function event(payload: Event, input: { directory: string; project?: string; workspace?: string }): GlobalEvent {
  20. return {
  21. directory: input.directory,
  22. project: input.project,
  23. workspace: input.workspace,
  24. payload,
  25. }
  26. }
  27. function vcs(branch: string): Event {
  28. return {
  29. id: `evt_vcs_${branch}`,
  30. type: "vcs.branch.updated",
  31. properties: {
  32. branch,
  33. },
  34. }
  35. }
  36. function update(version: string): Event {
  37. return {
  38. id: `evt_update_${version}`,
  39. type: "installation.update-available",
  40. properties: {
  41. version,
  42. },
  43. }
  44. }
  45. async function mount() {
  46. const events = createEventSource()
  47. const calls = createFetch()
  48. const seen: Event[] = []
  49. const workspaces: Array<string | undefined> = []
  50. let project!: ReturnType<typeof useProject>
  51. let done!: () => void
  52. const ready = new Promise<void>((resolve) => {
  53. done = resolve
  54. })
  55. const app = await testRender(() => (
  56. <TestTuiContexts>
  57. <SDKProvider url="http://test" directory={directory} events={events.source} fetch={calls.fetch}>
  58. <ProjectProvider>
  59. <Probe
  60. onReady={async (ctx) => {
  61. project = ctx.project
  62. await project.sync()
  63. done()
  64. }}
  65. seen={seen}
  66. workspaces={workspaces}
  67. />
  68. </ProjectProvider>
  69. </SDKProvider>
  70. </TestTuiContexts>
  71. ))
  72. await ready
  73. return { app, emit: events.emit, project, seen, workspaces }
  74. }
  75. function Probe(props: {
  76. seen: Event[]
  77. workspaces: Array<string | undefined>
  78. onReady: (ctx: { project: ReturnType<typeof useProject> }) => void
  79. }) {
  80. const project = useProject()
  81. const event = useEvent()
  82. onMount(() => {
  83. event.subscribe((evt, { workspace }) => {
  84. props.seen.push(evt)
  85. props.workspaces.push(workspace)
  86. })
  87. props.onReady({ project })
  88. })
  89. return <box />
  90. }
  91. describe("useEvent", () => {
  92. test("delivers events for the current project", async () => {
  93. const { app, emit, seen, workspaces } = await mount()
  94. try {
  95. emit(event(vcs("main"), { directory: "/tmp/other", project: projectID, workspace: "ws_a" }))
  96. await wait(() => seen.length === 1)
  97. expect(seen).toEqual([vcs("main")])
  98. expect(workspaces).toEqual(["ws_a"])
  99. } finally {
  100. app.renderer.destroy()
  101. }
  102. })
  103. test("delivers current project events regardless of active workspace", async () => {
  104. const { app, emit, project, seen } = await mount()
  105. try {
  106. project.workspace.set("ws_a")
  107. emit(event(vcs("ws"), { directory: "/tmp/other", project: projectID, workspace: "ws_b" }))
  108. await wait(() => seen.length === 1)
  109. expect(seen).toEqual([vcs("ws")])
  110. } finally {
  111. app.renderer.destroy()
  112. }
  113. })
  114. test("delivers truly global events even when a workspace is active", async () => {
  115. const { app, emit, project, seen } = await mount()
  116. try {
  117. project.workspace.set("ws_a")
  118. emit(event(update("1.2.3"), { directory: "global" }))
  119. await wait(() => seen.length === 1)
  120. expect(seen).toEqual([update("1.2.3")])
  121. } finally {
  122. app.renderer.destroy()
  123. }
  124. })
  125. })