124 lines
5.6 KiB
TypeScript
124 lines
5.6 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
|
|
import {
|
|
canNavigateConversationHistory,
|
|
createConversationTabHistory,
|
|
navigateConversationTabHistory,
|
|
removeConversationHistoryEntry,
|
|
syncConversationTabHistory,
|
|
} from "../src/react-app/domains/session/chat/conversation-tab-history";
|
|
|
|
function entry(sessionId: string, splitSessionId: string | null = null) {
|
|
return { sessionId, splitSessionId };
|
|
}
|
|
|
|
describe("conversation tab history", () => {
|
|
test("records visits without duplicate adjacent entries", () => {
|
|
let history = createConversationTabHistory("workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-b");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-b");
|
|
|
|
expect(history.entries).toEqual([entry("session-a"), entry("session-b")]);
|
|
expect(history.index).toBe(1);
|
|
});
|
|
|
|
test("navigates back and forward without wrapping", () => {
|
|
let history = createConversationTabHistory("workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-b");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-c");
|
|
|
|
expect(canNavigateConversationHistory(history, "back")).toBe(true);
|
|
expect(canNavigateConversationHistory(history, "forward")).toBe(false);
|
|
|
|
const back = navigateConversationTabHistory(history, "back");
|
|
expect(back.entry).toEqual(entry("session-b"));
|
|
expect(back.history.index).toBe(1);
|
|
|
|
const secondBack = navigateConversationTabHistory(back.history, "back");
|
|
expect(secondBack.entry).toEqual(entry("session-a"));
|
|
expect(canNavigateConversationHistory(secondBack.history, "back")).toBe(false);
|
|
|
|
const noWrapBack = navigateConversationTabHistory(secondBack.history, "back");
|
|
expect(noWrapBack.entry).toBeNull();
|
|
expect(noWrapBack.history).toBe(secondBack.history);
|
|
|
|
const forward = navigateConversationTabHistory(secondBack.history, "forward");
|
|
expect(forward.entry).toEqual(entry("session-b"));
|
|
});
|
|
|
|
test("clears the forward branch after a new selection", () => {
|
|
let history = createConversationTabHistory("workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-b");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-c");
|
|
|
|
const back = navigateConversationTabHistory(history, "back");
|
|
const branched = syncConversationTabHistory(back.history, "workspace-a", "session-d");
|
|
|
|
expect(branched.entries).toEqual([entry("session-a"), entry("session-b"), entry("session-d")]);
|
|
expect(branched.index).toBe(2);
|
|
expect(canNavigateConversationHistory(branched, "forward")).toBe(false);
|
|
});
|
|
|
|
test("resets when the workspace changes", () => {
|
|
let history = createConversationTabHistory("workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-b");
|
|
|
|
const reset = syncConversationTabHistory(history, "workspace-b", "session-c");
|
|
|
|
expect(reset.workspaceId).toBe("workspace-b");
|
|
expect(reset.entries).toEqual([entry("session-c")]);
|
|
expect(reset.index).toBe(0);
|
|
});
|
|
|
|
test("records split state as its own history step", () => {
|
|
let history = createConversationTabHistory("workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-a", "session-b");
|
|
|
|
expect(history.entries).toEqual([entry("session-a"), entry("session-a", "session-b")]);
|
|
expect(history.index).toBe(1);
|
|
|
|
const back = navigateConversationTabHistory(history, "back");
|
|
expect(back.entry).toEqual(entry("session-a", null));
|
|
|
|
const forward = navigateConversationTabHistory(back.history, "forward");
|
|
expect(forward.entry).toEqual(entry("session-a", "session-b"));
|
|
});
|
|
|
|
test("removes an inactive closed entry without moving the current session", () => {
|
|
let history = createConversationTabHistory("workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-b");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-c");
|
|
|
|
const closed = removeConversationHistoryEntry(history, "workspace-a", "session-b");
|
|
|
|
expect(closed.entries).toEqual([entry("session-a"), entry("session-c")]);
|
|
expect(closed.index).toBe(1);
|
|
expect(navigateConversationTabHistory(closed, "back").entry).toEqual(entry("session-a"));
|
|
});
|
|
|
|
test("removes an active closed entry and lands on the nearest remaining history", () => {
|
|
let history = createConversationTabHistory("workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-b");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-c");
|
|
history = navigateConversationTabHistory(history, "back").history;
|
|
|
|
const closed = removeConversationHistoryEntry(history, "workspace-a", "session-b");
|
|
|
|
expect(closed.entries).toEqual([entry("session-a"), entry("session-c")]);
|
|
expect(closed.index).toBe(0);
|
|
expect(canNavigateConversationHistory(closed, "back")).toBe(false);
|
|
expect(navigateConversationTabHistory(closed, "forward").entry).toEqual(entry("session-c"));
|
|
});
|
|
|
|
test("clears split references when the split session is removed", () => {
|
|
let history = createConversationTabHistory("workspace-a", "session-a");
|
|
history = syncConversationTabHistory(history, "workspace-a", "session-a", "session-b");
|
|
|
|
const closed = removeConversationHistoryEntry(history, "workspace-a", "session-b");
|
|
|
|
expect(closed.entries).toEqual([entry("session-a")]);
|
|
expect(closed.index).toBe(0);
|
|
});
|
|
});
|