fix(webui): persist large sidebar ordering state
This commit is contained in:
@@ -13,6 +13,7 @@ const getSessionAutomationsSpy = vi.fn<(key: string) => Promise<SessionAutomatio
|
||||
const toggleThemeSpy = vi.fn();
|
||||
const updateUrlSpy = vi.fn();
|
||||
const attachSpy = vi.fn();
|
||||
const setSidebarStateSpy = vi.fn();
|
||||
const runStatusHandlers = new Set<(chatId: string, startedAt: number | null) => void>();
|
||||
const sessionUpdateHandlers = new Set<(chatId: string, scope?: string) => void>();
|
||||
let mockSessions: ChatSummary[] = [];
|
||||
@@ -218,6 +219,7 @@ vi.mock("@/lib/nanobot-client", () => {
|
||||
sendMessage = vi.fn();
|
||||
newChat = vi.fn();
|
||||
attach = attachSpy;
|
||||
setSidebarState = setSidebarStateSpy;
|
||||
close = vi.fn();
|
||||
updateUrl = updateUrlSpy;
|
||||
updateMaxFrameBytes = vi.fn();
|
||||
@@ -245,6 +247,7 @@ describe("App layout", () => {
|
||||
getSessionAutomationsSpy.mockReset().mockResolvedValue([]);
|
||||
toggleThemeSpy.mockReset();
|
||||
attachSpy.mockReset();
|
||||
setSidebarStateSpy.mockReset();
|
||||
runStatusHandlers.clear();
|
||||
sessionUpdateHandlers.clear();
|
||||
window.history.replaceState(null, "", "/");
|
||||
@@ -1403,13 +1406,6 @@ describe("App layout", () => {
|
||||
if (href === "/api/webui/sidebar-state") {
|
||||
return { ok: true, json: async () => initialState };
|
||||
}
|
||||
if (href.startsWith("/api/webui/sidebar-state/update?")) {
|
||||
const encoded = new URLSearchParams(href.split("?", 2)[1]).get("state");
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => JSON.parse(encoded ?? "{}"),
|
||||
};
|
||||
}
|
||||
return { ok: false, status: 404 };
|
||||
}),
|
||||
);
|
||||
@@ -1429,12 +1425,11 @@ describe("App layout", () => {
|
||||
expect(within(sidebar).getByText("Archived")).toBeInTheDocument(),
|
||||
);
|
||||
expect(within(sidebar).getByRole("button", { name: /^First chat$/ })).toBeInTheDocument();
|
||||
const updateUrl = vi.mocked(fetch).mock.calls
|
||||
.map(([url]) => String(url))
|
||||
.find((url) => url.startsWith("/api/webui/sidebar-state/update?"));
|
||||
expect(updateUrl).toBeTruthy();
|
||||
const encoded = new URLSearchParams(updateUrl?.split("?", 2)[1]).get("state");
|
||||
expect(JSON.parse(encoded ?? "{}").view.show_archived).toBe(true);
|
||||
expect(setSidebarStateSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
view: expect.objectContaining({ show_archived: true }),
|
||||
}),
|
||||
);
|
||||
|
||||
expect(within(sidebar).queryByRole("button", { name: "View" })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { NanobotClient } from "@/lib/nanobot-client";
|
||||
import type { SidebarStatePayload } from "@/lib/types";
|
||||
|
||||
/**
|
||||
* Minimal fake WebSocket implementing the subset NanobotClient touches.
|
||||
@@ -935,6 +936,44 @@ describe("NanobotClient", () => {
|
||||
expect(client.hasUnsettledRun("chat-scope-control")).toBe(true);
|
||||
});
|
||||
|
||||
it("sends large sidebar ordering state outside the HTTP request line", () => {
|
||||
const client = new NanobotClient({
|
||||
url: "ws://test",
|
||||
reconnect: false,
|
||||
socketFactory: (url) => new FakeSocket(url) as unknown as WebSocket,
|
||||
});
|
||||
const sessionOrder = Array.from(
|
||||
{ length: 160 },
|
||||
(_, index) => `websocket:${index.toString().padStart(4, "0")}-${"x".repeat(48)}`,
|
||||
);
|
||||
const state: SidebarStatePayload = {
|
||||
schema_version: 1,
|
||||
pinned_keys: [],
|
||||
archived_keys: [],
|
||||
session_order: sessionOrder,
|
||||
title_overrides: {},
|
||||
project_name_overrides: {},
|
||||
tags_by_key: {},
|
||||
collapsed_groups: {},
|
||||
view: {
|
||||
density: "comfortable",
|
||||
show_previews: false,
|
||||
show_timestamps: false,
|
||||
show_archived: false,
|
||||
sort: "manual",
|
||||
},
|
||||
updated_at: null,
|
||||
};
|
||||
|
||||
client.connect();
|
||||
lastSocket().fakeOpen();
|
||||
client.setSidebarState(state);
|
||||
|
||||
const [serialized] = lastSocket().sent;
|
||||
expect(new TextEncoder().encode(serialized).byteLength).toBeGreaterThan(8_192);
|
||||
expect(JSON.parse(serialized)).toEqual({ type: "set_sidebar_state", state });
|
||||
});
|
||||
|
||||
it("does not correlate a new-chat scope rejection to an unrelated sent turn", async () => {
|
||||
const client = new NanobotClient({
|
||||
url: "ws://test",
|
||||
|
||||
Reference in New Issue
Block a user