| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- import { beforeAll, describe, expect, mock, test } from "bun:test"
- import { createRoot } from "solid-js"
- import type { LineComment } from "./comments"
- let createCommentSessionForTest: typeof import("./comments").createCommentSessionForTest
- beforeAll(async () => {
- mock.module("@solidjs/router", () => ({
- useNavigate: () => () => undefined,
- useParams: () => ({}),
- useLocation: () => ({}),
- useSearchParams: () => [{}, () => undefined],
- }))
- mock.module("@kirincode-ai/ui/context", () => ({
- createSimpleContext: () => ({
- use: () => undefined,
- provider: () => undefined,
- }),
- }))
- const mod = await import("./comments")
- createCommentSessionForTest = mod.createCommentSessionForTest
- })
- function line(file: string, id: string, time: number): LineComment {
- return {
- id,
- file,
- comment: id,
- time,
- selection: { start: 1, end: 1 },
- }
- }
- describe("comments session indexing", () => {
- test("keeps file list behavior and aggregate chronological order", () => {
- createRoot((dispose) => {
- const now = Date.now()
- const comments = createCommentSessionForTest({
- "a.ts": [line("a.ts", "a-late", now + 20_000), line("a.ts", "a-early", now + 1_000)],
- "b.ts": [line("b.ts", "b-mid", now + 10_000)],
- })
- expect(comments.list("a.ts").map((item) => item.id)).toEqual(["a-late", "a-early"])
- expect(comments.all().map((item) => item.id)).toEqual(["a-early", "b-mid", "a-late"])
- const next = comments.add({
- file: "b.ts",
- comment: "next",
- selection: { start: 2, end: 2 },
- })
- expect(comments.list("b.ts").at(-1)?.id).toBe(next.id)
- expect(comments.all().map((item) => item.time)).toEqual(
- comments
- .all()
- .map((item) => item.time)
- .slice()
- .sort((a, b) => a - b),
- )
- dispose()
- })
- })
- test("remove updates file and aggregate indexes consistently", () => {
- createRoot((dispose) => {
- const comments = createCommentSessionForTest({
- "a.ts": [line("a.ts", "a1", 10), line("a.ts", "shared", 20)],
- "b.ts": [line("b.ts", "shared", 30)],
- })
- comments.setFocus({ file: "a.ts", id: "shared" })
- comments.setActive({ file: "a.ts", id: "shared" })
- comments.remove("a.ts", "shared")
- expect(comments.list("a.ts").map((item) => item.id)).toEqual(["a1"])
- expect(
- comments
- .all()
- .filter((item) => item.id === "shared")
- .map((item) => item.file),
- ).toEqual(["b.ts"])
- expect(comments.focus()).toBeNull()
- expect(comments.active()).toEqual({ file: "a.ts", id: "shared" })
- dispose()
- })
- })
- test("clear resets file and aggregate indexes plus focus state", () => {
- createRoot((dispose) => {
- const comments = createCommentSessionForTest({
- "a.ts": [line("a.ts", "a1", 10)],
- })
- const next = comments.add({
- file: "b.ts",
- comment: "next",
- selection: { start: 2, end: 2 },
- })
- comments.setActive({ file: "b.ts", id: next.id })
- comments.clear()
- expect(comments.list("a.ts")).toEqual([])
- expect(comments.list("b.ts")).toEqual([])
- expect(comments.all()).toEqual([])
- expect(comments.focus()).toBeNull()
- expect(comments.active()).toBeNull()
- dispose()
- })
- })
- test("remove keeps focus when same comment id exists in another file", () => {
- createRoot((dispose) => {
- const comments = createCommentSessionForTest({
- "a.ts": [line("a.ts", "shared", 10)],
- "b.ts": [line("b.ts", "shared", 20)],
- })
- comments.setFocus({ file: "b.ts", id: "shared" })
- comments.remove("a.ts", "shared")
- expect(comments.focus()).toEqual({ file: "b.ts", id: "shared" })
- expect(comments.list("a.ts")).toEqual([])
- expect(comments.list("b.ts").map((item) => item.id)).toEqual(["shared"])
- dispose()
- })
- })
- test("setFocus and setActive updater callbacks receive current state", () => {
- createRoot((dispose) => {
- const comments = createCommentSessionForTest()
- comments.setFocus({ file: "a.ts", id: "a1" })
- comments.setFocus((current) => {
- expect(current).toEqual({ file: "a.ts", id: "a1" })
- return { file: "b.ts", id: "b1" }
- })
- comments.setActive({ file: "c.ts", id: "c1" })
- comments.setActive((current) => {
- expect(current).toEqual({ file: "c.ts", id: "c1" })
- return null
- })
- expect(comments.focus()).toEqual({ file: "b.ts", id: "b1" })
- expect(comments.active()).toBeNull()
- dispose()
- })
- })
- test("update changes only the targeted comment body", () => {
- createRoot((dispose) => {
- const comments = createCommentSessionForTest({
- "a.ts": [line("a.ts", "a1", 10), line("a.ts", "a2", 20)],
- })
- comments.update("a.ts", "a2", "edited")
- expect(comments.list("a.ts").map((item) => item.comment)).toEqual(["a1", "edited"])
- dispose()
- })
- })
- test("replace swaps comment state and clears focus state", () => {
- createRoot((dispose) => {
- const comments = createCommentSessionForTest({
- "a.ts": [line("a.ts", "a1", 10)],
- })
- comments.setFocus({ file: "a.ts", id: "a1" })
- comments.setActive({ file: "a.ts", id: "a1" })
- comments.replace([line("b.ts", "b1", 30)])
- expect(comments.list("a.ts")).toEqual([])
- expect(comments.list("b.ts").map((item) => item.id)).toEqual(["b1"])
- expect(comments.focus()).toBeNull()
- expect(comments.active()).toBeNull()
- dispose()
- })
- })
- })
|