Files
fusion/packages/dashboard/app/utils/__tests__/boardWorkflowsCache.test.ts
gsxdsm 869974cd5a FN-7383: refresh workflows after chat authoring
Chat-authored workflow changes now propagate immediately to workflow selectors and editors.

- Emit workflow lifecycle SSE events when chat, planner, or room workflow tools create, update, select, configure, or delete workflows.
- Force-refresh board workflow caches and the workflow editor list when workflow lifecycle events arrive.
- Add cross-surface tests for chat workflow creation visibility and room/chat workflow tool event behavior.
- Add a patch changeset for the published Fusion package.

Files changed:
 .changeset/fn-7383-chat-workflow-authoring.md      |  7 +++
 .../workflow-selection-cross-surface.test.tsx      | 23 +++++++++
 packages/dashboard/app/api/legacy.ts               |  9 ++--
 .../app/components/WorkflowNodeEditor.tsx          | 23 ++++++++-
 .../app/hooks/__tests__/useBoardWorkflows.test.ts  | 27 +++++++---
 packages/dashboard/app/hooks/useBoardWorkflows.ts  | 22 +++++---
 .../utils/__tests__/boardWorkflowsCache.test.ts    | 12 ++++-
 .../dashboard/app/utils/boardWorkflowsCache.ts     | 14 +++++
 .../dashboard/src/__tests__/chat-manager.test.ts   | 44 ++++++++++++++--
 .../dashboard/src/__tests__/chat.rooms.test.ts     | 44 ++++++++++++++++
 packages/dashboard/src/chat.ts                     | 59 +++++++++++++++++++---
 11 files changed, 255 insertions(+), 29 deletions(-)

Fusion-Task-Id: FN-7383

Fusion-Task-Lineage: ebe4c982-214b-402f-b829-a58ace19ffe0

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-01 11:12:59 -07:00

82 lines
2.7 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import type { BoardWorkflowsPayload } from "../../api";
import { clearBoardWorkflowsCache, readBoardWorkflowsCache, writeBoardWorkflowsCache } from "../boardWorkflowsCache";
const payload: BoardWorkflowsPayload = {
flagEnabled: true,
defaultWorkflowId: "builtin:coding",
workflows: [
{
id: "builtin:coding",
name: "Coding",
columns: [{ id: "todo", name: "Todo", flags: {} }],
},
],
taskWorkflowIds: { "FN-1": "builtin:coding" },
};
describe("boardWorkflowsCache", () => {
afterEach(() => {
if (typeof window !== "undefined") {
window.sessionStorage.clear();
}
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
it("round-trips board-workflows payloads per project", () => {
writeBoardWorkflowsCache("project-a", payload);
expect(readBoardWorkflowsCache("project-a")).toEqual(payload);
});
it("keeps project cache keys isolated", () => {
writeBoardWorkflowsCache("project-a", payload);
expect(readBoardWorkflowsCache("project-b")).toBeNull();
});
it("clears only the requested project cache after out-of-component workflow mutations", () => {
writeBoardWorkflowsCache("project-a", payload);
writeBoardWorkflowsCache("project-b", { ...payload, defaultWorkflowId: "wf-b" });
clearBoardWorkflowsCache("project-a");
expect(readBoardWorkflowsCache("project-a")).toBeNull();
expect(readBoardWorkflowsCache("project-b")).toEqual({ ...payload, defaultWorkflowId: "wf-b" });
});
it("returns null for missing, corrupt, or invalid entries", () => {
expect(readBoardWorkflowsCache("missing")).toBeNull();
window.sessionStorage.setItem("fusion:board-workflows:corrupt", "{");
expect(readBoardWorkflowsCache("corrupt")).toBeNull();
window.sessionStorage.setItem("fusion:board-workflows:invalid", JSON.stringify({ flagEnabled: true, workflows: {} }));
expect(readBoardWorkflowsCache("invalid")).toBeNull();
});
it("swallows sessionStorage write failures", () => {
vi.spyOn(window.sessionStorage.__proto__, "setItem").mockImplementation(() => {
throw new Error("quota");
});
expect(() => writeBoardWorkflowsCache("project-a", payload)).not.toThrow();
});
it("swallows sessionStorage read failures", () => {
vi.spyOn(window.sessionStorage.__proto__, "getItem").mockImplementation(() => {
throw new Error("private mode");
});
expect(readBoardWorkflowsCache("project-a")).toBeNull();
});
it("returns null without window for SSR callers", () => {
vi.stubGlobal("window", undefined);
expect(readBoardWorkflowsCache("project-a")).toBeNull();
expect(() => writeBoardWorkflowsCache("project-a", payload)).not.toThrow();
});
});