test(FN-4327): complete Step 4 — add direct visibility anchor regressions

Fusion-Task-Id: FN-4327
Fusion-Task-Lineage: 8508cca0-c7ff-43da-bbf1-385f9bc095ff
This commit is contained in:
Fusion
2026-05-13 08:34:31 -07:00
committed by gsxdsm
parent 24339f7d89
commit c199357c05
2 changed files with 149 additions and 0 deletions

View File

@@ -640,6 +640,32 @@ describe("ChatView — rooms (FN-3805..FN-3811 contract)", () => {
}
});
it("FN-4327: desktop visibility restore does not re-anchor active room thread", async () => {
const restoreMatchMedia = mockDesktopViewport();
const metrics = mockMessagesContainerMetrics({ scrollHeight: 1180, clientHeight: 240, initialScrollTop: 250 });
try {
setup({}, {
activeRoom: roomA,
messages: [{ id: "rmsg-1", roomId: roomA.id, role: "assistant", content: "One", createdAt: "2026-04-08T00:00:00.000Z", senderAgentId: "agent-1", mentions: [] }],
});
render(<ChatView projectId="proj-123" addToast={vi.fn()} experimentalFeatures={{ chatRooms: true }} />);
metrics.setScrollTop(300);
Object.defineProperty(document, "visibilityState", { configurable: true, value: "visible" });
fireEvent(document, new Event("visibilitychange"));
await waitFor(() => {
expect(metrics.getScrollTop()).toBe(300);
});
} finally {
metrics.restore();
restoreMatchMedia.mockRestore();
Object.defineProperty(document, "visibilityState", { configurable: true, value: "visible" });
}
});
it("keeps direct mode behavior unchanged when rooms are enabled", async () => {
localStorage.setItem("fusion:chat-scope", "direct");
const addToast = vi.fn();

View File

@@ -3017,6 +3017,42 @@ describe("Direct/Rooms scope toggle", () => {
expect(screen.getByTestId("chat-session-session-001")).toHaveClass("chat-session-item--active");
});
it("FN-4327: switching scope from Rooms to Direct re-anchors direct thread", async () => {
setupMockChat({
activeSession: { id: "session-001", agentId: "agent-001", status: "active", title: "Test Chat", createdAt: "2026-04-08T00:00:00.000Z", updatedAt: "2026-04-08T00:00:00.000Z" },
messages: [{ id: "msg-001", sessionId: "session-001", role: "assistant", content: "One", createdAt: "2026-04-08T00:00:00.000Z" }],
sessions: [{ id: "session-001", agentId: "agent-001", status: "active", title: "Test Chat", createdAt: "2026-04-08T00:00:00.000Z", updatedAt: "2026-04-08T00:00:00.000Z" }],
filteredSessions: [{ id: "session-001", agentId: "agent-001", status: "active", title: "Test Chat", createdAt: "2026-04-08T00:00:00.000Z", updatedAt: "2026-04-08T00:00:00.000Z" }],
});
render(<ChatView projectId="proj-123" addToast={vi.fn()} experimentalFeatures={{ chatRooms: true }} />);
const messagesContainer = document.querySelector(".chat-messages") as HTMLDivElement;
Object.defineProperty(messagesContainer, "scrollHeight", { configurable: true, get: () => 1200 });
Object.defineProperty(messagesContainer, "clientHeight", { configurable: true, get: () => 200 });
let scrollTopValue = 500;
Object.defineProperty(messagesContainer, "scrollTop", {
configurable: true,
get: () => scrollTopValue,
set: (value: number) => {
scrollTopValue = value;
},
});
fireEvent.scroll(messagesContainer);
expect(screen.getByTestId("chat-jump-to-latest")).toBeInTheDocument();
await userEvent.click(screen.getByTestId("chat-sidebar-scope-rooms"));
await userEvent.click(screen.getByTestId("chat-sidebar-scope-direct"));
await waitFor(() => {
expect(scrollTopValue).toBe(1200);
});
await waitFor(() => {
expect(screen.queryByTestId("chat-jump-to-latest")).not.toBeInTheDocument();
});
});
it("forces direct scope when localStorage persisted rooms but chatRooms is off", () => {
setupMockChat({ sessions: [], filteredSessions: [] });
localStorage.setItem("fusion:chat-scope", "rooms");
@@ -4004,6 +4040,93 @@ describe("ChatView mobile behavior", () => {
}
});
it("FN-4327: desktop visibility restore re-anchors direct chat and resets jump-to-latest state", async () => {
const restoreMatchMedia = mockDesktopViewport();
try {
setupMockChat({
activeSession: activeSessionFixture,
messages: [{ id: "msg-001", sessionId: "session-001", role: "assistant", content: "One", createdAt: "2026-04-08T00:00:00.000Z" }],
});
const { rerender } = render(<ChatView projectId="proj-123" addToast={vi.fn()} />);
const messagesContainer = document.querySelector(".chat-messages") as HTMLDivElement;
let scrollTopValue = 600;
let scrollHeightValue = 1200;
Object.defineProperty(messagesContainer, "scrollHeight", { configurable: true, get: () => scrollHeightValue });
Object.defineProperty(messagesContainer, "clientHeight", { configurable: true, get: () => 200 });
Object.defineProperty(messagesContainer, "scrollTop", {
configurable: true,
get: () => scrollTopValue,
set: (value: number) => {
scrollTopValue = value;
},
});
fireEvent.scroll(messagesContainer);
expect(screen.getByTestId("chat-jump-to-latest")).toBeInTheDocument();
Object.defineProperty(document, "visibilityState", { configurable: true, value: "visible" });
fireEvent(document, new Event("visibilitychange"));
await waitFor(() => {
expect(scrollTopValue).toBe(1200);
});
await waitFor(() => {
expect(screen.queryByTestId("chat-jump-to-latest")).not.toBeInTheDocument();
});
setupMockChat({
activeSession: activeSessionFixture,
messages: [
{ id: "msg-001", sessionId: "session-001", role: "assistant", content: "One", createdAt: "2026-04-08T00:00:00.000Z" },
{ id: "msg-002", sessionId: "session-001", role: "assistant", content: "Two", createdAt: "2026-04-08T00:00:10.000Z" },
],
});
scrollTopValue = 700;
scrollHeightValue = 1300;
rerender(<ChatView projectId="proj-123" addToast={vi.fn()} />);
await waitFor(() => {
expect(scrollTopValue).toBe(1300);
});
} finally {
restoreMatchMedia.mockRestore();
Object.defineProperty(document, "visibilityState", { configurable: true, value: "visible" });
}
});
it("FN-4327: desktop pageshow re-anchors direct chat thread", async () => {
const restoreMatchMedia = mockDesktopViewport();
try {
setupMockChat({
activeSession: activeSessionFixture,
messages: [{ id: "msg-001", sessionId: "session-001", role: "assistant", content: "One", createdAt: "2026-04-08T00:00:00.000Z" }],
});
render(<ChatView projectId="proj-123" addToast={vi.fn()} />);
const messagesContainer = document.querySelector(".chat-messages") as HTMLDivElement;
let scrollTopValue = 420;
Object.defineProperty(messagesContainer, "scrollHeight", { configurable: true, get: () => 1280 });
Object.defineProperty(messagesContainer, "scrollTop", {
configurable: true,
get: () => scrollTopValue,
set: (value: number) => {
scrollTopValue = value;
},
});
fireEvent(window, new Event("pageshow"));
await waitFor(() => {
expect(scrollTopValue).toBe(1280);
});
} finally {
restoreMatchMedia.mockRestore();
}
});
it("FN-3884: retries bottom anchor while container height keeps growing", async () => {
const restoreMatchMedia = mockDesktopViewport();
const originalRaf = window.requestAnimationFrame;