feat(FN-4849): complete Step 2 — route room thread reads and sends
Fusion-Task-Id: FN-4849 Fusion-Task-Lineage: 624f86c4-0b0f-4543-90d2-381f890da401
This commit is contained in:
committed by
gsxdsm
parent
2adc9fa0da
commit
b97709a3d4
@@ -1129,7 +1129,19 @@ export function QuickChatFAB({
|
||||
}, [chatMode, selectedAgentId, targetModelSelection]);
|
||||
|
||||
const hasChatTarget = chatMode === "agent" ? Boolean(selectedAgentId) : Boolean(targetModelSelection);
|
||||
const inputDisabled = !hasChatTarget || !activeSession;
|
||||
const roomThreadActive = chatRoomsEnabled && Boolean(roomsState.activeRoom);
|
||||
const displayedMessages = useMemo<ChatMessageInfo[]>(() => {
|
||||
if (!roomThreadActive) {
|
||||
return messages;
|
||||
}
|
||||
return roomsState.messages.map((message) => ({
|
||||
id: message.id,
|
||||
role: message.role,
|
||||
content: message.content,
|
||||
toolCalls: undefined,
|
||||
}));
|
||||
}, [messages, roomThreadActive, roomsState.messages]);
|
||||
const inputDisabled = roomThreadActive ? false : (!hasChatTarget || !activeSession);
|
||||
const hasPersistedAgentSessionSelection = useMemo(
|
||||
() => Boolean(selectedAgentId) && sessions.some((session) => !session.modelProvider && !session.modelId && session.agentId === selectedAgentId),
|
||||
[selectedAgentId, sessions],
|
||||
@@ -1578,16 +1590,16 @@ export function QuickChatFAB({
|
||||
}, [anchorToBottom]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const sessionId = activeSession?.id ?? null;
|
||||
const threadId = roomThreadActive ? (roomsState.activeRoom?.id ?? null) : (activeSession?.id ?? null);
|
||||
const previousState = previousOpenStateRef.current;
|
||||
previousOpenStateRef.current = { isOpen, sessionId };
|
||||
previousOpenStateRef.current = { isOpen, sessionId: threadId };
|
||||
|
||||
if (!isOpen || !sessionId) {
|
||||
if (!isOpen || !threadId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const openingNow = !previousState.isOpen && isOpen;
|
||||
const sessionChangedWhileOpen = previousState.isOpen && previousState.sessionId !== sessionId;
|
||||
const sessionChangedWhileOpen = previousState.isOpen && previousState.sessionId !== threadId;
|
||||
if (!openingNow && !sessionChangedWhileOpen) {
|
||||
return;
|
||||
}
|
||||
@@ -1596,7 +1608,7 @@ export function QuickChatFAB({
|
||||
if (!messagesEl) return;
|
||||
|
||||
anchorToBottom(messagesEl);
|
||||
}, [isOpen, activeSession?.id, anchorToBottom]);
|
||||
}, [isOpen, activeSession?.id, anchorToBottom, roomThreadActive, roomsState.activeRoom?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile || !isOpen || !activeSession) {
|
||||
@@ -1633,7 +1645,7 @@ export function QuickChatFAB({
|
||||
if (!isUserScrollingRef.current) {
|
||||
scrollToBottom();
|
||||
}
|
||||
}, [messages, streamingText, streamingThinking, isStreaming, isOpen, scrollToBottom]);
|
||||
}, [displayedMessages, streamingText, streamingThinking, isStreaming, isOpen, roomThreadActive, scrollToBottom]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeSession?.id) {
|
||||
@@ -1716,7 +1728,7 @@ export function QuickChatFAB({
|
||||
const showRoomGroups = chatRoomsEnabled && roomOptions.length > 0;
|
||||
|
||||
const activeSessionLabel = useMemo(() => {
|
||||
if (showRoomGroups && roomsState.activeRoom) {
|
||||
if (showRoomGroups && roomThreadActive && roomsState.activeRoom) {
|
||||
return `#${roomsState.activeRoom.name}`;
|
||||
}
|
||||
const activeOption = sessionOptions.find((option) => option.id === activeSession?.id);
|
||||
@@ -1727,7 +1739,7 @@ export function QuickChatFAB({
|
||||
return "Loading sessions…";
|
||||
}
|
||||
return "Select a session";
|
||||
}, [activeSession?.id, roomsState.activeRoom, sessionOptions, sessionsLoading, showRoomGroups]);
|
||||
}, [activeSession?.id, roomThreadActive, roomsState.activeRoom, sessionOptions, sessionsLoading, showRoomGroups]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
@@ -1763,7 +1775,7 @@ export function QuickChatFAB({
|
||||
}, [sessionMenuOpen]);
|
||||
|
||||
const inputPlaceholder = useMemo(() => {
|
||||
if (chatRoomsEnabled && roomsState.activeRoom) {
|
||||
if (roomThreadActive && roomsState.activeRoom) {
|
||||
return `Message #${roomsState.activeRoom.name}`;
|
||||
}
|
||||
if (chatMode === "agent") {
|
||||
@@ -1777,7 +1789,7 @@ export function QuickChatFAB({
|
||||
return `Message ${selectedModelTag}`;
|
||||
}
|
||||
return "Select a model to start chatting";
|
||||
}, [chatMode, chatRoomsEnabled, roomsState.activeRoom?.name, selectedAgent, selectedModelTag]);
|
||||
}, [chatMode, roomThreadActive, roomsState.activeRoom?.name, selectedAgent, selectedModelTag]);
|
||||
|
||||
const handleSessionSwitch = useCallback((sessionId: string) => {
|
||||
const selectedSession = sessions.find((session) => session.id === sessionId);
|
||||
@@ -1785,7 +1797,7 @@ export function QuickChatFAB({
|
||||
return;
|
||||
}
|
||||
|
||||
if (chatRoomsEnabled && roomsState.activeRoom) {
|
||||
if (roomThreadActive) {
|
||||
roomsState.selectRoom(null);
|
||||
}
|
||||
|
||||
@@ -1808,7 +1820,7 @@ export function QuickChatFAB({
|
||||
|
||||
void selectSession(selectedSession);
|
||||
setSessionMenuOpen(false);
|
||||
}, [chatRoomsEnabled, markRead, roomsState, selectSession, sessions]);
|
||||
}, [markRead, roomThreadActive, roomsState, selectSession, sessions]);
|
||||
|
||||
const handleRoomSwitch = useCallback((roomId: string) => {
|
||||
const selectedRoom = roomsState.rooms.find((room) => room.id === roomId);
|
||||
@@ -1926,9 +1938,14 @@ export function QuickChatFAB({
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomThreadActive && attachmentsToSend.length > 0) {
|
||||
addToast("Attachments are not supported in chat rooms yet", "warning");
|
||||
focusComposerInput();
|
||||
preserveComposerFocusRef.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (trimmed === "/clear" || trimmed === "/new") {
|
||||
stopStreaming();
|
||||
clearPendingMessage();
|
||||
attachmentsToSend.forEach((attachment) => {
|
||||
if (attachment.previewUrl) {
|
||||
URL.revokeObjectURL(attachment.previewUrl);
|
||||
@@ -1937,13 +1954,20 @@ export function QuickChatFAB({
|
||||
setPendingAttachments((previous) => previous.filter((attachment) => !attachmentsToSend.includes(attachment)));
|
||||
|
||||
try {
|
||||
if (chatMode === "model") {
|
||||
if (roomThreadActive && roomsState.activeRoom?.id) {
|
||||
await roomsState.clearRoom(roomsState.activeRoom.id);
|
||||
setHelpMessageVisible(false);
|
||||
} else if (chatMode === "model") {
|
||||
stopStreaming();
|
||||
clearPendingMessage();
|
||||
const parsed = parseModelSelection(resolvedModelSelection);
|
||||
if (!parsed) {
|
||||
return;
|
||||
}
|
||||
await startFreshSession(FN_AGENT_ID, parsed.modelProvider, parsed.modelId);
|
||||
} else if (selectedAgentId) {
|
||||
stopStreaming();
|
||||
clearPendingMessage();
|
||||
await startFreshSession(selectedAgentId);
|
||||
}
|
||||
} catch {
|
||||
@@ -1957,7 +1981,11 @@ export function QuickChatFAB({
|
||||
|
||||
try {
|
||||
setHelpMessageVisible(false);
|
||||
await sendMessage(trimmed, attachmentsToSend.map((attachment) => attachment.file));
|
||||
if (roomThreadActive) {
|
||||
await roomsState.sendRoomMessage(trimmed);
|
||||
} else {
|
||||
await sendMessage(trimmed, attachmentsToSend.map((attachment) => attachment.file));
|
||||
}
|
||||
attachmentsToSend.forEach((attachment) => {
|
||||
if (attachment.previewUrl) {
|
||||
URL.revokeObjectURL(attachment.previewUrl);
|
||||
@@ -1978,6 +2006,8 @@ export function QuickChatFAB({
|
||||
inputDisabled,
|
||||
messageInput,
|
||||
resolvedModelSelection,
|
||||
roomThreadActive,
|
||||
roomsState,
|
||||
selectedAgentId,
|
||||
sendMessage,
|
||||
startFreshSession,
|
||||
@@ -2492,7 +2522,7 @@ export function QuickChatFAB({
|
||||
<div className="quick-chat-panel-header">
|
||||
<div className="quick-chat-panel-title-wrap">
|
||||
<h3>Quick Chat</h3>
|
||||
{chatRoomsEnabled && roomsState.activeRoom ? (
|
||||
{roomThreadActive && roomsState.activeRoom ? (
|
||||
<span className="quick-chat-model-tag" data-testid="quick-chat-room-tag" title={`#${roomsState.activeRoom.name}`}>
|
||||
#{roomsState.activeRoom.name}
|
||||
</span>
|
||||
@@ -2553,7 +2583,7 @@ export function QuickChatFAB({
|
||||
<input
|
||||
type="hidden"
|
||||
data-testid="quick-chat-session-dropdown"
|
||||
value={showRoomGroups && roomsState.activeRoom ? "" : activeSession?.id ?? ""}
|
||||
value={showRoomGroups && roomThreadActive ? "" : activeSession?.id ?? ""}
|
||||
readOnly
|
||||
/>
|
||||
<button
|
||||
@@ -2565,7 +2595,7 @@ export function QuickChatFAB({
|
||||
data-testid="quick-chat-session-dropdown-trigger"
|
||||
onClick={() => setSessionMenuOpen((current) => !current)}
|
||||
>
|
||||
{chatRoomsEnabled && roomsState.activeRoom ? (
|
||||
{roomThreadActive && roomsState.activeRoom ? (
|
||||
<Hash size={16} aria-hidden="true" />
|
||||
) : activeSession?.modelProvider ? (
|
||||
<ProviderIcon provider={activeSession.modelProvider} size="sm" />
|
||||
@@ -2608,7 +2638,7 @@ export function QuickChatFAB({
|
||||
</>
|
||||
)}
|
||||
{sessionOptions.map((sessionOption) => {
|
||||
const isActiveSession = roomsState.activeRoom === null && activeSession?.id === sessionOption.id;
|
||||
const isActiveSession = !roomThreadActive && activeSession?.id === sessionOption.id;
|
||||
const session = sessions.find((item) => item.id === sessionOption.id);
|
||||
const showUnreadDot = !isActiveSession && isUnread("direct", sessionOption.id, session?.lastMessageAt ?? session?.updatedAt);
|
||||
return (
|
||||
@@ -2717,9 +2747,9 @@ export function QuickChatFAB({
|
||||
<div className="quick-chat-panel-messages" ref={messagesRef} data-testid="quick-chat-messages" onScroll={updateScrollState}>
|
||||
{sessionsLoading ? (
|
||||
<div className="quick-chat-panel-empty">Loading conversation…</div>
|
||||
) : isStreaming ? (
|
||||
) : !roomThreadActive && isStreaming ? (
|
||||
<>
|
||||
{messages.map((message: ChatMessageInfo) => (
|
||||
{displayedMessages.map((message: ChatMessageInfo) => (
|
||||
<QuickChatMessageItem
|
||||
key={message.id}
|
||||
message={message}
|
||||
@@ -2767,13 +2797,35 @@ export function QuickChatFAB({
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : messagesLoading ? (
|
||||
) : roomThreadActive ? roomsState.messagesLoading ? (
|
||||
<div className="quick-chat-panel-empty">Loading conversation…</div>
|
||||
) : messages.length === 0 && !streamingText && !streamingThinking && !isStreaming && !helpMessageVisible ? (
|
||||
) : displayedMessages.length === 0 && !helpMessageVisible ? (
|
||||
<div className="quick-chat-panel-empty">No messages yet. Start the conversation!</div>
|
||||
) : (
|
||||
<>
|
||||
{messages.map((message: ChatMessageInfo) => (
|
||||
{displayedMessages.map((message: ChatMessageInfo) => (
|
||||
<QuickChatMessageItem
|
||||
key={message.id}
|
||||
message={message}
|
||||
forcePlain={message.role !== "user" && plainTextMessageIds.has(message.id)}
|
||||
mentionAgentsByName={mentionAgentsByName}
|
||||
roomContext={roomContext}
|
||||
onToggleRender={toggleMessageRenderMode}
|
||||
/>
|
||||
))}
|
||||
{helpMessageVisible && (
|
||||
<div className="quick-chat-panel-message quick-chat-panel-message--received" data-testid="quick-chat-help-message">
|
||||
{renderAssistantMessageContent("Available commands:\n- `/new` or `/clear` — Clear conversation and start fresh\n- `/skill:{name}` — Use a specific skill\n- `/help` — Show this help")}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : messagesLoading ? (
|
||||
<div className="quick-chat-panel-empty">Loading conversation…</div>
|
||||
) : displayedMessages.length === 0 && !streamingText && !streamingThinking && !isStreaming && !helpMessageVisible ? (
|
||||
<div className="quick-chat-panel-empty">No messages yet. Start the conversation!</div>
|
||||
) : (
|
||||
<>
|
||||
{displayedMessages.map((message: ChatMessageInfo) => (
|
||||
<QuickChatMessageItem
|
||||
key={message.id}
|
||||
message={message}
|
||||
@@ -3018,7 +3070,7 @@ export function QuickChatFAB({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{pendingMessage && (
|
||||
{!roomThreadActive && pendingMessage && (
|
||||
<div className="chat-pending-message" data-testid="chat-pending-indicator">
|
||||
<span>{`Queued: ${pendingPreview}`}</span>
|
||||
<button
|
||||
|
||||
@@ -1444,4 +1444,162 @@ describe("QuickChatFAB session-first UX", () => {
|
||||
|
||||
expect(openFile).toHaveBeenCalledWith("packages/dashboard/app/components/QuickChatFAB.tsx", { line: undefined, col: undefined });
|
||||
});
|
||||
|
||||
describe("FN-4849 room switching", () => {
|
||||
const room = {
|
||||
id: "room-1",
|
||||
name: "engineering",
|
||||
slug: "engineering",
|
||||
memberCount: 2,
|
||||
createdAt: "2026-05-16T00:00:00.000Z",
|
||||
updatedAt: "2026-05-16T00:00:03.000Z",
|
||||
};
|
||||
|
||||
it("switching to a room renders room messages, not session messages", async () => {
|
||||
const selectRoom = vi.fn();
|
||||
mockUseAppSettings.mockReturnValue({ experimentalFeatures: { chatRooms: true } } as ReturnType<typeof useAppSettings>);
|
||||
mockUseChatRooms.mockReturnValue({
|
||||
rooms: [room],
|
||||
roomsLoading: false,
|
||||
roomsError: null,
|
||||
activeRoom: room,
|
||||
activeRoomMembers: [],
|
||||
messages: [
|
||||
{ id: "room-msg-1", roomId: room.id, role: "assistant", content: "room msg 1", createdAt: "2026-05-16T00:00:01.000Z" },
|
||||
{ id: "room-msg-2", roomId: room.id, role: "user", content: "room msg 2", createdAt: "2026-05-16T00:00:02.000Z" },
|
||||
],
|
||||
messagesLoading: false,
|
||||
selectRoom,
|
||||
createRoom: vi.fn(),
|
||||
deleteRoom: vi.fn(),
|
||||
sendRoomMessage: vi.fn(),
|
||||
clearRoom: vi.fn(),
|
||||
refreshRooms: vi.fn(),
|
||||
});
|
||||
mockFetchChatMessages.mockResolvedValueOnce({
|
||||
messages: [{ id: "session-msg", sessionId: "session-model", role: "assistant", content: "hello from session", createdAt: "2026-05-16T00:00:00.000Z" }],
|
||||
});
|
||||
|
||||
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
||||
|
||||
const messages = await screen.findByTestId("quick-chat-messages");
|
||||
expect(messages).toHaveTextContent("room msg 1");
|
||||
expect(messages).toHaveTextContent("room msg 2");
|
||||
expect(messages).not.toHaveTextContent("hello from session");
|
||||
expect(screen.getByTestId("quick-chat-session-dropdown-trigger")).toHaveTextContent("#engineering");
|
||||
expect(screen.getByTestId("quick-chat-input")).toHaveAttribute("placeholder", "Message #engineering");
|
||||
});
|
||||
|
||||
it("sending while in a room routes to sendRoomMessage, not sendMessage", async () => {
|
||||
const sendRoomMessage = vi.fn().mockResolvedValue(undefined);
|
||||
mockUseAppSettings.mockReturnValue({ experimentalFeatures: { chatRooms: true } } as ReturnType<typeof useAppSettings>);
|
||||
mockUseChatRooms.mockReturnValue({
|
||||
rooms: [room], roomsLoading: false, roomsError: null, activeRoom: room, activeRoomMembers: [], messages: [], messagesLoading: false,
|
||||
selectRoom: vi.fn(), createRoom: vi.fn(), deleteRoom: vi.fn(), sendRoomMessage, clearRoom: vi.fn(), refreshRooms: vi.fn(),
|
||||
});
|
||||
|
||||
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
||||
|
||||
const input = await screen.findByTestId("quick-chat-input");
|
||||
fireEvent.change(input, { target: { value: "room dispatch" } });
|
||||
fireEvent.click(screen.getByTestId("quick-chat-send"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(sendRoomMessage).toHaveBeenCalledWith("room dispatch");
|
||||
});
|
||||
expect(mockStreamChatResponse).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("/clear while in a room calls clearRoom, not startFreshSession", async () => {
|
||||
const clearRoom = vi.fn().mockResolvedValue(undefined);
|
||||
mockUseAppSettings.mockReturnValue({ experimentalFeatures: { chatRooms: true } } as ReturnType<typeof useAppSettings>);
|
||||
mockUseChatRooms.mockReturnValue({
|
||||
rooms: [room], roomsLoading: false, roomsError: null, activeRoom: room, activeRoomMembers: [], messages: [], messagesLoading: false,
|
||||
selectRoom: vi.fn(), createRoom: vi.fn(), deleteRoom: vi.fn(), sendRoomMessage: vi.fn(), clearRoom, refreshRooms: vi.fn(),
|
||||
});
|
||||
|
||||
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
||||
|
||||
const input = await screen.findByTestId("quick-chat-input");
|
||||
fireEvent.change(input, { target: { value: "/clear" } });
|
||||
fireEvent.click(screen.getByTestId("quick-chat-send"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(clearRoom).toHaveBeenCalledWith("room-1");
|
||||
});
|
||||
expect(mockCreateChatSession).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("composer is enabled in a room even without an activeSession", async () => {
|
||||
mockFetchResumeChatSession.mockRejectedValueOnce(new Error("resume failed"));
|
||||
mockUseAppSettings.mockReturnValue({ experimentalFeatures: { chatRooms: true } } as ReturnType<typeof useAppSettings>);
|
||||
mockUseChatRooms.mockReturnValue({
|
||||
rooms: [room], roomsLoading: false, roomsError: null, activeRoom: room, activeRoomMembers: [], messages: [], messagesLoading: false,
|
||||
selectRoom: vi.fn(), createRoom: vi.fn(), deleteRoom: vi.fn(), sendRoomMessage: vi.fn(), clearRoom: vi.fn(), refreshRooms: vi.fn(),
|
||||
});
|
||||
|
||||
render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
||||
|
||||
expect(await screen.findByTestId("quick-chat-input")).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it("switching back from a room to a session restores session messages", async () => {
|
||||
const selectRoom = vi.fn();
|
||||
mockUseAppSettings.mockReturnValue({ experimentalFeatures: { chatRooms: true } } as ReturnType<typeof useAppSettings>);
|
||||
mockUseChatRooms.mockReturnValue({
|
||||
rooms: [room], roomsLoading: false, roomsError: null, activeRoom: room, activeRoomMembers: [],
|
||||
messages: [{ id: "room-msg-1", roomId: room.id, role: "assistant", content: "room msg 1", createdAt: "2026-05-16T00:00:01.000Z" }],
|
||||
messagesLoading: false,
|
||||
selectRoom, createRoom: vi.fn(), deleteRoom: vi.fn(), sendRoomMessage: vi.fn(), clearRoom: vi.fn(), refreshRooms: vi.fn(),
|
||||
});
|
||||
mockFetchChatMessages.mockResolvedValueOnce({
|
||||
messages: [{ id: "session-msg", sessionId: "session-model", role: "assistant", content: "hello from session", createdAt: "2026-05-16T00:00:00.000Z" }],
|
||||
});
|
||||
|
||||
const view = render(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
||||
fireEvent.click(await screen.findByTestId("quick-chat-session-dropdown-trigger"));
|
||||
fireEvent.click(screen.getByTestId("quick-chat-session-option-session-model"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(selectRoom).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
mockUseChatRooms.mockReturnValue({
|
||||
rooms: [room], roomsLoading: false, roomsError: null, activeRoom: null, activeRoomMembers: [],
|
||||
messages: [], messagesLoading: false,
|
||||
selectRoom, createRoom: vi.fn(), deleteRoom: vi.fn(), sendRoomMessage: vi.fn(), clearRoom: vi.fn(), refreshRooms: vi.fn(),
|
||||
});
|
||||
view.rerender(<QuickChatFAB addToast={vi.fn()} projectId="proj-1" />);
|
||||
expect(await screen.findByTestId("quick-chat-messages")).toHaveTextContent("hello from session");
|
||||
});
|
||||
|
||||
it("attachment-in-room is blocked with a warning toast", async () => {
|
||||
const addToast = vi.fn();
|
||||
const sendRoomMessage = vi.fn().mockResolvedValue(undefined);
|
||||
mockUseAppSettings.mockReturnValue({ experimentalFeatures: { chatRooms: true } } as ReturnType<typeof useAppSettings>);
|
||||
mockUseChatRooms.mockReturnValue({
|
||||
rooms: [room], roomsLoading: false, roomsError: null, activeRoom: room, activeRoomMembers: [], messages: [], messagesLoading: false,
|
||||
selectRoom: vi.fn(), createRoom: vi.fn(), deleteRoom: vi.fn(), sendRoomMessage, clearRoom: vi.fn(), refreshRooms: vi.fn(),
|
||||
});
|
||||
|
||||
render(<QuickChatFAB addToast={addToast} projectId="proj-1" />);
|
||||
fireEvent.click(screen.getByTestId("quick-chat-fab"));
|
||||
|
||||
const attachmentInput = document.querySelector(".quick-chat-attachment-input") as HTMLInputElement | null;
|
||||
const input = screen.getByTestId("quick-chat-input");
|
||||
const file = new File(["hi"], "note.txt", { type: "text/plain" });
|
||||
expect(attachmentInput).not.toBeNull();
|
||||
fireEvent.change(attachmentInput!, { target: { files: [file] } });
|
||||
fireEvent.change(input, { target: { value: "try send" } });
|
||||
fireEvent.click(screen.getByTestId("quick-chat-send"));
|
||||
|
||||
expect(addToast).toHaveBeenCalledWith("Attachments are not supported in chat rooms yet", "warning");
|
||||
expect(sendRoomMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user