Files
kandev/apps/web/lib/task-listing/view-preference.test.ts
Jérémy Coatelen 538813de02 feat(web): add Threads view for live agent conversations (#3112)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
2026-09-03 08:07:16 +00:00

107 lines
3.7 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import {
getEffectiveTaskListingView,
getStoredTaskListingView,
parseTaskListingView,
resolveHomeTaskListingRedirect,
resolveTaskListingView,
setStoredTaskListingView,
TASK_LISTING_VIEW_CHANGE_EVENT,
TASK_LISTING_VIEW_STORAGE_KEY,
} from "./view-preference";
let originalLocalStorage: Storage | null = null;
afterEach(() => {
if (originalLocalStorage) {
Object.defineProperty(window, "localStorage", {
configurable: true,
value: originalLocalStorage,
});
originalLocalStorage = null;
}
window.localStorage.clear();
vi.restoreAllMocks();
});
describe("task listing view preference", () => {
it("accepts the versioned List preference", () => {
expect(parseTaskListingView('"list"')).toBe("list");
});
it("ignores malformed values", () => {
expect(parseTaskListingView("pipeline")).toBeNull();
});
it("uses the legacy Pipeline mode only when no device preference exists", () => {
expect(resolveTaskListingView(null, "graph2")).toBe("pipeline");
});
it("defaults malformed device preferences to Kanban instead of using the legacy mode", () => {
window.localStorage.setItem(TASK_LISTING_VIEW_STORAGE_KEY, '"unsupported"');
expect(resolveTaskListingView(getStoredTaskListingView(), "graph2")).toBe("kanban");
});
it("defaults to Kanban when neither a device nor legacy preference exists", () => {
expect(resolveTaskListingView(null, null)).toBe("kanban");
});
it("prefers a device-local preference over the legacy mode", () => {
expect(resolveTaskListingView("kanban", "graph2")).toBe("kanban");
});
it("renders Pipeline as Kanban on phone without changing the preference", () => {
expect(getEffectiveTaskListingView("pipeline", true)).toBe("kanban");
});
it("writes the versioned local preference and notifies this document", () => {
const listener = vi.fn();
window.addEventListener(TASK_LISTING_VIEW_CHANGE_EVENT, listener);
setStoredTaskListingView("pipeline");
expect(window.localStorage.getItem(TASK_LISTING_VIEW_STORAGE_KEY)).toBe('"pipeline"');
expect(listener).toHaveBeenCalledOnce();
window.removeEventListener(TASK_LISTING_VIEW_CHANGE_EVENT, listener);
});
it("keeps the selected view for this interaction when storage rejects the write", () => {
originalLocalStorage = window.localStorage;
Object.defineProperty(window, "localStorage", {
configurable: true,
value: {
getItem: () => null,
setItem: () => {
throw new Error("storage blocked");
},
clear: () => {},
},
});
setStoredTaskListingView("pipeline");
expect(getStoredTaskListingView()).toBe("pipeline");
});
it("accepts the versioned Threads preference", () => {
expect(parseTaskListingView('"threads"')).toBe("threads");
});
it("keeps Threads on phone because its columns page one at a time", () => {
expect(getEffectiveTaskListingView("threads", true)).toBe("threads");
});
it("routes Home to the view that owns its own page", () => {
expect(resolveHomeTaskListingRedirect("list", undefined, undefined)).toBe("list");
expect(resolveHomeTaskListingRedirect("threads", undefined, undefined)).toBe("threads");
expect(resolveHomeTaskListingRedirect("kanban", undefined, undefined)).toBeNull();
expect(resolveHomeTaskListingRedirect("pipeline", undefined, undefined)).toBeNull();
});
it("never redirects Home away from an explicitly opened task or session", () => {
expect(resolveHomeTaskListingRedirect("threads", "task-1", undefined)).toBeNull();
expect(resolveHomeTaskListingRedirect("threads", undefined, "session-1")).toBeNull();
});
});