From efb26c1d2eb6e6b2f3ed6e3fd9aa7e4dc037131a Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Wed, 27 May 2026 13:58:20 -0300 Subject: [PATCH 1/3] regression: Reply in other channel actions not working if ?msg= is present --- .../client/components/message/toolbar/useReplyInDMAction.ts | 3 ++- apps/meteor/client/lib/chats/flows/replyBroadcast.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/meteor/client/components/message/toolbar/useReplyInDMAction.ts b/apps/meteor/client/components/message/toolbar/useReplyInDMAction.ts index dd10e3f56c6af..be05dad70f103 100644 --- a/apps/meteor/client/components/message/toolbar/useReplyInDMAction.ts +++ b/apps/meteor/client/components/message/toolbar/useReplyInDMAction.ts @@ -71,11 +71,12 @@ export const useReplyInDMAction = ( context: ['message', 'message-mobile', 'threads', 'federated'], type: 'communication', action() { + const { msg: _, ...searchParameters } = router.getSearchParameters(); roomCoordinator.openRouteLink( 'd', { name: message.u.username }, { - ...router.getSearchParameters(), + ...searchParameters, reply: message._id, }, ); diff --git a/apps/meteor/client/lib/chats/flows/replyBroadcast.ts b/apps/meteor/client/lib/chats/flows/replyBroadcast.ts index 69e857ee57cb3..9d58654ae647a 100644 --- a/apps/meteor/client/lib/chats/flows/replyBroadcast.ts +++ b/apps/meteor/client/lib/chats/flows/replyBroadcast.ts @@ -5,11 +5,12 @@ import { roomCoordinator } from '../../rooms/roomCoordinator'; import type { ChatAPI } from '../ChatAPI'; export const replyBroadcast = async (_chat: ChatAPI, message: IMessage) => { + const { msg: _, ...searchParameters } = router.getSearchParameters(); roomCoordinator.openRouteLink( 'd', { name: message.u.username }, { - ...router.getSearchParameters(), + ...searchParameters, reply: message._id, }, ); From 3a822f35b7e42d22d6102433fcc24a877eccf04a Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Wed, 27 May 2026 14:07:41 -0300 Subject: [PATCH 2/3] test: add test --- .../toolbar/useReplyInDMAction.spec.ts | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 apps/meteor/client/components/message/toolbar/useReplyInDMAction.spec.ts diff --git a/apps/meteor/client/components/message/toolbar/useReplyInDMAction.spec.ts b/apps/meteor/client/components/message/toolbar/useReplyInDMAction.spec.ts new file mode 100644 index 0000000000000..5542098c4c6fd --- /dev/null +++ b/apps/meteor/client/components/message/toolbar/useReplyInDMAction.spec.ts @@ -0,0 +1,86 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { renderHook } from '@testing-library/react'; + +import { useReplyInDMAction } from './useReplyInDMAction'; +import { createFakeMessage, createFakeRoom, createFakeSubscription, createFakeUser } from '../../../../tests/mocks/data'; +import { roomCoordinator } from '../../../lib/rooms/roomCoordinator'; + +jest.mock('../../../lib/rooms/roomCoordinator', () => ({ + roomCoordinator: { + openRouteLink: jest.fn(), + }, +})); + +const mockedOpenRouteLink = jest.mocked(roomCoordinator.openRouteLink); + +const currentUser = createFakeUser({ + _id: 'current-user-id', + username: 'currentuser', +}); + +const messageAuthor = { + _id: 'author-user-id', + username: 'authoruser', + name: 'Author User', +}; + +const message = createFakeMessage({ + _id: 'reply-message-id', + u: messageAuthor, +}); + +const room = createFakeRoom({ + _id: 'channel-id', + t: 'c', + name: 'general', +}); + +const subscription = createFakeSubscription({ + rid: 'channel-id', + t: 'c', +}); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('useReplyInDMAction', () => { + it('should not carry over the msg search parameter when opening a direct message', () => { + const getSearchParameters = jest.fn().mockReturnValue({ + msg: 'stale-message-id', + layout: 'embedded', + }); + + const { result } = renderHook(() => useReplyInDMAction(message, { room, subscription }), { + wrapper: mockAppRoot().withUser(currentUser).withPermission('create-d').withRouter({ getSearchParameters }).build(), + }); + + expect(result.current).not.toBeNull(); + + result.current?.action({ stopPropagation: jest.fn() } as unknown as UIEvent); + + expect(getSearchParameters).toHaveBeenCalled(); + expect(mockedOpenRouteLink).toHaveBeenCalledWith( + 'd', + { name: messageAuthor.username }, + { + layout: 'embedded', + reply: 'reply-message-id', + }, + ); + + const searchParams = mockedOpenRouteLink.mock.calls[0]?.[2]; + expect(searchParams).not.toHaveProperty('msg'); + }); + + it('should return null when already in a direct message room', () => { + const dmRoom = createFakeRoom({ t: 'd' }); + const dmSubscription = createFakeSubscription({ t: 'd' }); + + const { result } = renderHook(() => useReplyInDMAction(message, { room: dmRoom, subscription: dmSubscription }), { + wrapper: mockAppRoot().withUser(currentUser).withPermission('create-d').build(), + }); + + expect(result.current).toBeNull(); + }); +}); From 2a36b932fbef3b858cedc46307615e81ed14d37f Mon Sep 17 00:00:00 2001 From: MartinSchoeler Date: Wed, 27 May 2026 14:14:34 -0300 Subject: [PATCH 3/3] test: more tests --- .../lib/chats/flows/replyBroadcast.spec.ts | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 apps/meteor/client/lib/chats/flows/replyBroadcast.spec.ts diff --git a/apps/meteor/client/lib/chats/flows/replyBroadcast.spec.ts b/apps/meteor/client/lib/chats/flows/replyBroadcast.spec.ts new file mode 100644 index 0000000000000..d94a9df5ff95e --- /dev/null +++ b/apps/meteor/client/lib/chats/flows/replyBroadcast.spec.ts @@ -0,0 +1,59 @@ +import { replyBroadcast } from './replyBroadcast'; +import { createFakeMessage } from '../../../../tests/mocks/data'; +import { router } from '../../../providers/RouterProvider'; +import { roomCoordinator } from '../../rooms/roomCoordinator'; +import type { ChatAPI } from '../ChatAPI'; + +jest.mock('../../rooms/roomCoordinator', () => ({ + roomCoordinator: { + openRouteLink: jest.fn(), + }, +})); + +jest.mock('../../../providers/RouterProvider', () => ({ + router: { + getSearchParameters: jest.fn(), + }, +})); + +const mockedOpenRouteLink = jest.mocked(roomCoordinator.openRouteLink); +const mockedGetSearchParameters = jest.mocked(router.getSearchParameters); + +const messageAuthor = { + _id: 'author-user-id', + username: 'authoruser', + name: 'Author User', +}; + +const message = createFakeMessage({ + _id: 'reply-message-id', + u: messageAuthor, +}); + +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('replyBroadcast', () => { + it('should not carry over the msg search parameter when opening a direct message', async () => { + mockedGetSearchParameters.mockReturnValue({ + msg: 'stale-message-id', + layout: 'embedded', + }); + + await replyBroadcast({} as ChatAPI, message); + + expect(mockedGetSearchParameters).toHaveBeenCalled(); + expect(mockedOpenRouteLink).toHaveBeenCalledWith( + 'd', + { name: messageAuthor.username }, + { + layout: 'embedded', + reply: 'reply-message-id', + }, + ); + + const searchParams = mockedOpenRouteLink.mock.calls[0]?.[2]; + expect(searchParams).not.toHaveProperty('msg'); + }); +});