Files
openwork/apps/app/tests/conversation-tab-history.test.ts

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);
});
});