comments.test.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { beforeAll, describe, expect, mock, test } from "bun:test"
  2. import { createRoot } from "solid-js"
  3. import type { LineComment } from "./comments"
  4. let createCommentSessionForTest: typeof import("./comments").createCommentSessionForTest
  5. beforeAll(async () => {
  6. mock.module("@solidjs/router", () => ({
  7. useNavigate: () => () => undefined,
  8. useParams: () => ({}),
  9. useLocation: () => ({}),
  10. useSearchParams: () => [{}, () => undefined],
  11. }))
  12. mock.module("@kirincode-ai/ui/context", () => ({
  13. createSimpleContext: () => ({
  14. use: () => undefined,
  15. provider: () => undefined,
  16. }),
  17. }))
  18. const mod = await import("./comments")
  19. createCommentSessionForTest = mod.createCommentSessionForTest
  20. })
  21. function line(file: string, id: string, time: number): LineComment {
  22. return {
  23. id,
  24. file,
  25. comment: id,
  26. time,
  27. selection: { start: 1, end: 1 },
  28. }
  29. }
  30. describe("comments session indexing", () => {
  31. test("keeps file list behavior and aggregate chronological order", () => {
  32. createRoot((dispose) => {
  33. const now = Date.now()
  34. const comments = createCommentSessionForTest({
  35. "a.ts": [line("a.ts", "a-late", now + 20_000), line("a.ts", "a-early", now + 1_000)],
  36. "b.ts": [line("b.ts", "b-mid", now + 10_000)],
  37. })
  38. expect(comments.list("a.ts").map((item) => item.id)).toEqual(["a-late", "a-early"])
  39. expect(comments.all().map((item) => item.id)).toEqual(["a-early", "b-mid", "a-late"])
  40. const next = comments.add({
  41. file: "b.ts",
  42. comment: "next",
  43. selection: { start: 2, end: 2 },
  44. })
  45. expect(comments.list("b.ts").at(-1)?.id).toBe(next.id)
  46. expect(comments.all().map((item) => item.time)).toEqual(
  47. comments
  48. .all()
  49. .map((item) => item.time)
  50. .slice()
  51. .sort((a, b) => a - b),
  52. )
  53. dispose()
  54. })
  55. })
  56. test("remove updates file and aggregate indexes consistently", () => {
  57. createRoot((dispose) => {
  58. const comments = createCommentSessionForTest({
  59. "a.ts": [line("a.ts", "a1", 10), line("a.ts", "shared", 20)],
  60. "b.ts": [line("b.ts", "shared", 30)],
  61. })
  62. comments.setFocus({ file: "a.ts", id: "shared" })
  63. comments.setActive({ file: "a.ts", id: "shared" })
  64. comments.remove("a.ts", "shared")
  65. expect(comments.list("a.ts").map((item) => item.id)).toEqual(["a1"])
  66. expect(
  67. comments
  68. .all()
  69. .filter((item) => item.id === "shared")
  70. .map((item) => item.file),
  71. ).toEqual(["b.ts"])
  72. expect(comments.focus()).toBeNull()
  73. expect(comments.active()).toEqual({ file: "a.ts", id: "shared" })
  74. dispose()
  75. })
  76. })
  77. test("clear resets file and aggregate indexes plus focus state", () => {
  78. createRoot((dispose) => {
  79. const comments = createCommentSessionForTest({
  80. "a.ts": [line("a.ts", "a1", 10)],
  81. })
  82. const next = comments.add({
  83. file: "b.ts",
  84. comment: "next",
  85. selection: { start: 2, end: 2 },
  86. })
  87. comments.setActive({ file: "b.ts", id: next.id })
  88. comments.clear()
  89. expect(comments.list("a.ts")).toEqual([])
  90. expect(comments.list("b.ts")).toEqual([])
  91. expect(comments.all()).toEqual([])
  92. expect(comments.focus()).toBeNull()
  93. expect(comments.active()).toBeNull()
  94. dispose()
  95. })
  96. })
  97. test("remove keeps focus when same comment id exists in another file", () => {
  98. createRoot((dispose) => {
  99. const comments = createCommentSessionForTest({
  100. "a.ts": [line("a.ts", "shared", 10)],
  101. "b.ts": [line("b.ts", "shared", 20)],
  102. })
  103. comments.setFocus({ file: "b.ts", id: "shared" })
  104. comments.remove("a.ts", "shared")
  105. expect(comments.focus()).toEqual({ file: "b.ts", id: "shared" })
  106. expect(comments.list("a.ts")).toEqual([])
  107. expect(comments.list("b.ts").map((item) => item.id)).toEqual(["shared"])
  108. dispose()
  109. })
  110. })
  111. test("setFocus and setActive updater callbacks receive current state", () => {
  112. createRoot((dispose) => {
  113. const comments = createCommentSessionForTest()
  114. comments.setFocus({ file: "a.ts", id: "a1" })
  115. comments.setFocus((current) => {
  116. expect(current).toEqual({ file: "a.ts", id: "a1" })
  117. return { file: "b.ts", id: "b1" }
  118. })
  119. comments.setActive({ file: "c.ts", id: "c1" })
  120. comments.setActive((current) => {
  121. expect(current).toEqual({ file: "c.ts", id: "c1" })
  122. return null
  123. })
  124. expect(comments.focus()).toEqual({ file: "b.ts", id: "b1" })
  125. expect(comments.active()).toBeNull()
  126. dispose()
  127. })
  128. })
  129. test("update changes only the targeted comment body", () => {
  130. createRoot((dispose) => {
  131. const comments = createCommentSessionForTest({
  132. "a.ts": [line("a.ts", "a1", 10), line("a.ts", "a2", 20)],
  133. })
  134. comments.update("a.ts", "a2", "edited")
  135. expect(comments.list("a.ts").map((item) => item.comment)).toEqual(["a1", "edited"])
  136. dispose()
  137. })
  138. })
  139. test("replace swaps comment state and clears focus state", () => {
  140. createRoot((dispose) => {
  141. const comments = createCommentSessionForTest({
  142. "a.ts": [line("a.ts", "a1", 10)],
  143. })
  144. comments.setFocus({ file: "a.ts", id: "a1" })
  145. comments.setActive({ file: "a.ts", id: "a1" })
  146. comments.replace([line("b.ts", "b1", 30)])
  147. expect(comments.list("a.ts")).toEqual([])
  148. expect(comments.list("b.ts").map((item) => item.id)).toEqual(["b1"])
  149. expect(comments.focus()).toBeNull()
  150. expect(comments.active()).toBeNull()
  151. dispose()
  152. })
  153. })
  154. })