Skip to content

Commit 25a10b6

Browse files
fix: Global Search "Jump to message" failing due to incorrect URL generation (#37717)
Co-authored-by: Douglas Fabris <27704687+dougfabris@users.noreply.github.com>
1 parent d821cd3 commit 25a10b6

File tree

8 files changed

+129
-26
lines changed

8 files changed

+129
-26
lines changed

.changeset/olive-pens-think.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rocket.chat/meteor': patch
3+
---
4+
5+
Fixes incorrect URL generation in Global Search "Jump to message" feature, resolving navigation issues when jumping to messages across different channels.

apps/meteor/client/lib/rooms/roomCoordinator.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,18 @@ class RoomCoordinatorClient extends RoomCoordinator {
6767
roomType: RoomType,
6868
subData: RoomIdentification,
6969
queryParams?: Record<string, string>,
70-
options: { replace?: boolean } = {},
70+
options: { replace?: boolean; routeParamsOverrides?: Record<string, string> } = {},
7171
): void {
7272
const config = this.getRoomTypeConfig(roomType);
7373
if (!config?.route) {
7474
return;
7575
}
7676

77-
let routeData = {};
77+
let _routeData = {};
7878
if (config.route.link) {
79-
routeData = config.route.link(subData);
79+
_routeData = config.route.link(subData);
8080
} else if (subData?.name) {
81-
routeData = {
81+
_routeData = {
8282
name: subData.name,
8383
};
8484
} else {
@@ -88,7 +88,7 @@ class RoomCoordinatorClient extends RoomCoordinator {
8888
router.navigate(
8989
{
9090
pattern: config.route.path ?? '/home',
91-
params: routeData,
91+
params: { ..._routeData, ...options.routeParamsOverrides },
9292
search: queryParams,
9393
},
9494
options,

apps/meteor/client/lib/utils/goToRoomById.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ import { roomCoordinator } from '../rooms/roomCoordinator';
88

99
const getRoomById = memoize((rid: IRoom['_id']) => callWithErrorHandling('getRoomById', rid));
1010

11-
export const goToRoomById = async (rid: IRoom['_id']): Promise<void> => {
11+
export type GoToRoomByIdOptions = {
12+
replace?: boolean;
13+
routeParamsOverrides?: Record<string, string>;
14+
};
15+
16+
export const goToRoomById = async (rid: IRoom['_id'], options: GoToRoomByIdOptions = {}): Promise<void> => {
1217
if (!rid) {
1318
return;
1419
}
1520

1621
const subscription = Subscriptions.state.find((record) => record.rid === rid);
1722

1823
if (subscription) {
19-
roomCoordinator.openRouteLink(subscription.t, subscription, router.getSearchParameters());
24+
roomCoordinator.openRouteLink(subscription.t, subscription, router.getSearchParameters(), options);
2025
return;
2126
}
2227

2328
const room = await getRoomById(rid);
24-
roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }, router.getSearchParameters());
29+
roomCoordinator.openRouteLink(room.t, { rid: room._id, ...room }, router.getSearchParameters(), options);
2530
};

apps/meteor/client/lib/utils/legacyJumpToMessage.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { isThreadMessage } from '@rocket.chat/core-typings';
44
import { goToRoomById } from './goToRoomById';
55
import { RoomHistoryManager } from '../../../app/ui-utils/client';
66
import { router } from '../../providers/RouterProvider';
7-
import { Rooms } from '../../stores';
87
import { RoomManager } from '../RoomManager';
98

109
/** @deprecated */
@@ -15,24 +14,12 @@ export const legacyJumpToMessage = async (message: IMessage) => {
1514
if (tab === 'thread' && (context === message.tmid || context === message._id)) {
1615
return;
1716
}
18-
router.navigate(
19-
{
20-
name: router.getRouteName()!,
21-
params: {
22-
tab: 'thread',
23-
context: message.tmid || message._id,
24-
rid: message.rid,
25-
name: Rooms.state.get(message.rid)?.name ?? '',
26-
},
27-
search: {
28-
...router.getSearchParameters(),
29-
msg: message._id,
30-
},
31-
},
32-
{ replace: false },
33-
);
34-
await RoomHistoryManager.getSurroundingMessages(message);
3517

18+
await goToRoomById(message.rid, {
19+
routeParamsOverrides: { tab: 'thread', context: message.tmid || message._id },
20+
replace: RoomManager.opened === message.rid,
21+
});
22+
await RoomHistoryManager.getSurroundingMessages(message);
3623
return;
3724
}
3825

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import type { IMessage } from '@rocket.chat/core-typings';
2+
import { Random } from '@rocket.chat/random';
3+
4+
import { Users } from './fixtures/userStates';
5+
import { HomeChannel } from './page-objects';
6+
import { setSettingValueById } from './utils';
7+
import type { BaseTest } from './utils/test';
8+
import { expect, test } from './utils/test';
9+
10+
test.use({ storageState: Users.admin.state });
11+
12+
test.describe.serial('Global Search', () => {
13+
let targetChannel: { name: string; _id: string };
14+
let targetGroup: { name: string; _id: string };
15+
let threadMessage: IMessage;
16+
let poHomeChannel: HomeChannel;
17+
18+
const fillMessages = async (api: BaseTest['api']) => {
19+
const { message: parentMessage } = await (
20+
await api.post('/chat.postMessage', { roomId: targetChannel._id, text: 'This is main message in channel' })
21+
).json();
22+
23+
const { message: childMessage } = await (
24+
await api.post('/chat.postMessage', { roomId: targetChannel._id, text: `This is thread message in channel`, tmid: parentMessage._id })
25+
).json();
26+
threadMessage = childMessage;
27+
};
28+
29+
test.beforeAll(async ({ api }) => {
30+
await Promise.all([
31+
api
32+
.post('/channels.create', { name: Random.id() })
33+
.then((res) => res.json())
34+
.then((data) => {
35+
targetChannel = data.channel;
36+
}),
37+
api
38+
.post('/groups.create', {
39+
name: Random.id(),
40+
extraData: { encrypted: false },
41+
})
42+
.then((res) => res.json())
43+
.then((data) => {
44+
targetGroup = data.group;
45+
}),
46+
setSettingValueById(api, 'Search.defaultProvider.GlobalSearchEnabled', true),
47+
]);
48+
await fillMessages(api);
49+
});
50+
51+
test.afterAll(({ api }) =>
52+
Promise.all([
53+
api.post('/channels.delete', { roomId: targetChannel._id }),
54+
api.post('/groups.delete', { roomId: targetGroup._id }),
55+
setSettingValueById(api, 'Search.defaultProvider.GlobalSearchEnabled', false),
56+
]),
57+
);
58+
59+
test.beforeEach(async ({ page }) => {
60+
poHomeChannel = new HomeChannel(page);
61+
await page.goto('/home');
62+
});
63+
64+
test('should open the correct message when jumping from global search in group to channel thread', async ({ page }) => {
65+
await poHomeChannel.sidenav.openChat(targetGroup.name);
66+
await poHomeChannel.roomToolbar.btnSearchMessages.click();
67+
68+
await poHomeChannel.tabs.searchMessages.search(threadMessage.msg.slice(10), { global: true }); // fill partial text to match search
69+
70+
const message = await poHomeChannel.tabs.searchMessages.getResultItem(threadMessage.msg);
71+
await message.hover();
72+
const jumpToMessageButton = message.getByRole('button', { name: 'Jump to message' });
73+
await jumpToMessageButton.click();
74+
75+
await expect(page.locator('header').getByRole('button').filter({ hasText: targetChannel.name })).toBeVisible(); // match channel name in room header
76+
await expect(page.getByText(threadMessage.msg)).toBeVisible();
77+
});
78+
});

apps/meteor/tests/e2e/page-objects/fragments/home-flextab.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { HomeFlextabNotificationPreferences } from './home-flextab-notificationP
77
import { HomeFlextabOtr } from './home-flextab-otr';
88
import { HomeFlextabPruneMessages } from './home-flextab-pruneMessages';
99
import { HomeFlextabRoom } from './home-flextab-room';
10+
import { SearchMessagesFlexTab } from './searchMessages-flextab';
1011

1112
export class HomeFlextab {
1213
private readonly page: Page;
@@ -25,6 +26,8 @@ export class HomeFlextab {
2526

2627
readonly pruneMessages: HomeFlextabPruneMessages;
2728

29+
readonly searchMessages: SearchMessagesFlexTab;
30+
2831
constructor(page: Page) {
2932
this.page = page;
3033
this.members = new HomeFlextabMembers(page);
@@ -34,6 +37,7 @@ export class HomeFlextab {
3437
this.otr = new HomeFlextabOtr(page);
3538
this.exportMessages = new ExportMessagesTab(page);
3639
this.pruneMessages = new HomeFlextabPruneMessages(page);
40+
this.searchMessages = new SearchMessagesFlexTab(page);
3741
}
3842

3943
get toolbarPrimaryActions(): Locator {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { Page } from '@playwright/test';
2+
3+
import { FlexTab } from './flextab';
4+
5+
export class SearchMessagesFlexTab extends FlexTab {
6+
constructor(page: Page) {
7+
super(page.getByRole('dialog', { name: 'Search Messages' }));
8+
}
9+
10+
async search(text: string, { global = false }: { global?: boolean } = {}) {
11+
if (global) {
12+
await this.root.getByText('Global search').click();
13+
}
14+
await this.root.getByPlaceholder('Search Messages').fill(text);
15+
}
16+
17+
async getResultItem(messageText: string) {
18+
return this.root.getByRole('listitem', { name: messageText });
19+
}
20+
}

apps/meteor/tests/e2e/page-objects/fragments/toolbar.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ export class RoomToolbar extends Toolbar {
6060
return this.root.getByRole('button', { name: 'Options' });
6161
}
6262

63+
get btnSearchMessages(): Locator {
64+
return this.root.getByRole('button', { name: 'Search Messages' });
65+
}
66+
6367
get btnDisableE2EEncryption(): Locator {
6468
return this.root.getByRole('button', { name: 'Disable E2E encryption' });
6569
}

0 commit comments

Comments
 (0)