+17





![opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)

![opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)
![opencode-agent[bot]](/assets/img/avatar_default.png)
opencode-agent[bot]
opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com>
LukeParkerDev
opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Aiden Cline
Brendan Allan
Aarav Sareen
Julian Coy
Brendan Allan
usrnk1
opencode
Vladimir Glafirov
Adam
Frank
Jay
Dustin Deus
Kit Langton
James Long
Simon Klee
Jay
Jack
David Hill
Aiden Cline
James Long
冯基魁
Aiden Cline
Victor Navarro
43ecf3ff1b
Co-authored-by: opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com> Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Co-authored-by: Aarav Sareen <96787824+arvsrn@users.noreply.github.com> Co-authored-by: Julian Coy <julian@ex-machina.co> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: usrnk1 <7547651+usrnk1@users.noreply.github.com> Co-authored-by: opencode <opencode@sst.dev> Co-authored-by: Vladimir Glafirov <vglafirov@gitlab.com> Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com> Co-authored-by: Frank <frank@anoma.ly> Co-authored-by: Jay <53023+jayair@users.noreply.github.com> Co-authored-by: Dustin Deus <deusdustin@gmail.com> Co-authored-by: Kit Langton <kit.langton@gmail.com> Co-authored-by: James Long <longster@gmail.com> Co-authored-by: Simon Klee <hello@simonklee.dk> Co-authored-by: Jay <air@live.ca> Co-authored-by: Jack <jack@anoma.ly> Co-authored-by: David Hill <1879069+iamdavidhill@users.noreply.github.com> Co-authored-by: Aiden Cline <aidenpcline@gmail.com> Co-authored-by: James Long <jlongster@users.noreply.github.com> Co-authored-by: 冯基魁 <56265583+fengjikui@users.noreply.github.com> Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com> Co-authored-by: Victor Navarro <vn4varro@gmail.com>
108 lines
3.6 KiB
TypeScript
108 lines
3.6 KiB
TypeScript
import { describe, expect, test } from "bun:test"
|
|
import { createRoot, getOwner, onCleanup } from "solid-js"
|
|
import { createTabMemory } from "./tab-memory"
|
|
import { nextTabAfterClose, pushClosedTab, removeClosedTabs, takeClosedTab, type ClosedTab } from "./closed-tabs"
|
|
import type { SessionTab, Tab } from "./tabs"
|
|
import type { ServerConnection } from "./server"
|
|
|
|
const server = "local\nhttp://localhost:4096" as ServerConnection.Key
|
|
|
|
function sessionTab(sessionId: string): SessionTab {
|
|
return { type: "session", server, sessionId }
|
|
}
|
|
|
|
describe("tab memory", () => {
|
|
test("keeps state until its tab is removed", () => {
|
|
createRoot((dispose) => {
|
|
const memory = createTabMemory(getOwner())
|
|
let disposed = 0
|
|
const first = memory.ensure("tab", "prompt", () => {
|
|
onCleanup(() => disposed++)
|
|
return { value: "prompt" }
|
|
})
|
|
|
|
expect(memory.ensure("tab", "prompt", () => ({ value: "other" }))).toBe(first)
|
|
expect(memory.get<typeof first>("tab", "prompt")).toBe(first)
|
|
expect(memory.get("missing", "prompt")).toBeUndefined()
|
|
expect(memory.ensure("other", "prompt", () => ({ value: "other" }))).not.toBe(first)
|
|
|
|
memory.remove("tab")
|
|
expect(disposed).toBe(1)
|
|
expect(memory.ensure("tab", "prompt", () => ({ value: "new" }))).not.toBe(first)
|
|
dispose()
|
|
})
|
|
})
|
|
})
|
|
|
|
describe("closed tab stack", () => {
|
|
test("records session tabs with their index", () => {
|
|
const stack = pushClosedTab([], sessionTab("a"), 2)
|
|
|
|
expect(stack).toEqual([{ tab: sessionTab("a"), index: 2 }])
|
|
})
|
|
|
|
test("ignores draft tabs", () => {
|
|
const draft: Tab = { type: "draft", draftID: "d1", server, directory: "/tmp" }
|
|
|
|
expect(pushClosedTab([], draft, 0)).toEqual([])
|
|
})
|
|
|
|
test("caps the stack size", () => {
|
|
const stack = Array.from({ length: 30 }, (_, i) => i).reduce<ClosedTab[]>(
|
|
(acc, i) => pushClosedTab(acc, sessionTab(`s${i}`), i),
|
|
[],
|
|
)
|
|
|
|
expect(stack).toHaveLength(25)
|
|
expect(stack[0]?.tab.sessionId).toBe("s5")
|
|
expect(stack.at(-1)?.tab.sessionId).toBe("s29")
|
|
})
|
|
|
|
test("pops the most recently closed tab", () => {
|
|
const stack = [
|
|
{ tab: sessionTab("a"), index: 0 },
|
|
{ tab: sessionTab("b"), index: 1 },
|
|
]
|
|
const result = takeClosedTab(stack, [])
|
|
|
|
expect(result.entry?.tab.sessionId).toBe("b")
|
|
expect(result.stack).toEqual([{ tab: sessionTab("a"), index: 0 }])
|
|
})
|
|
|
|
test("skips entries whose tab is already open", () => {
|
|
const stack = [
|
|
{ tab: sessionTab("a"), index: 0 },
|
|
{ tab: sessionTab("b"), index: 1 },
|
|
]
|
|
const result = takeClosedTab(stack, [sessionTab("b")])
|
|
|
|
expect(result.entry?.tab.sessionId).toBe("a")
|
|
expect(result.stack).toEqual([])
|
|
})
|
|
|
|
test("returns no entry when everything is open or empty", () => {
|
|
expect(takeClosedTab([], []).entry).toBeUndefined()
|
|
|
|
const result = takeClosedTab([{ tab: sessionTab("a"), index: 0 }], [sessionTab("a")])
|
|
expect(result.entry).toBeUndefined()
|
|
expect(result.stack).toEqual([])
|
|
})
|
|
|
|
test("purges removed sessions", () => {
|
|
const stack = [
|
|
{ tab: sessionTab("a"), index: 0 },
|
|
{ tab: sessionTab("b"), index: 1 },
|
|
]
|
|
|
|
expect(removeClosedTabs(stack, server, ["a"])).toEqual([{ tab: sessionTab("b"), index: 1 }])
|
|
})
|
|
|
|
test("does not navigate when a background tab closes", () => {
|
|
const tabs = [sessionTab("a"), sessionTab("b"), sessionTab("c")]
|
|
|
|
expect(nextTabAfterClose(tabs, 1, false)).toBeUndefined()
|
|
expect(nextTabAfterClose(tabs, 1, true)).toEqual(sessionTab("c"))
|
|
expect(nextTabAfterClose([sessionTab("a")], 0, true)).toBeNull()
|
|
})
|
|
})
|