diff --git a/apps/meteor/client/views/room/providers/ComposerPopupProvider.spec.tsx b/apps/meteor/client/views/room/providers/ComposerPopupProvider.spec.tsx new file mode 100644 index 0000000000000..9a3fe8bf72060 --- /dev/null +++ b/apps/meteor/client/views/room/providers/ComposerPopupProvider.spec.tsx @@ -0,0 +1,102 @@ +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { render, waitFor } from '@testing-library/react'; +import { useEffect } from 'react'; + +import ComposerPopupProvider from './ComposerPopupProvider'; +import { useComposerPopupOptions } from '../contexts/ComposerPopupContext'; +import FakeRoomProvider from '../../../../tests/mocks/client/FakeRoomProvider'; +import { createFakeRoom } from '../../../../tests/mocks/data'; + +const mockGrantedPermissions = new Set(); + +jest.mock('../../../../app/authorization/client', () => ({ + hasAtLeastOnePermission: (permissions: string[] | string) => { + const permissionList = Array.isArray(permissions) ? permissions : [permissions]; + + return permissionList.some((permission) => mockGrantedPermissions.has(permission)); + }, +})); + +jest.mock('../../../../app/utils/client', () => ({ + slashCommands: { + commands: {}, + }, +})); + +type PopupOptionsConsumerProps = { + onReady: (options: ReturnType) => void; +}; + +const PopupOptionsConsumer = ({ onReady }: PopupOptionsConsumerProps) => { + const options = useComposerPopupOptions(); + + useEffect(() => { + onReady(options); + }, [onReady, options]); + + return null; +}; + +const renderProvider = async (permissions: string[] = []) => { + mockGrantedPermissions.clear(); + permissions.forEach((permission) => mockGrantedPermissions.add(permission)); + + const room = createFakeRoom({ t: 'c' }); + const appRoot = permissions.reduce((wrapper, permission) => wrapper.withPermission(permission), mockAppRoot().withJohnDoe()).build(); + + let popupOptions: ReturnType | undefined; + + render( + + + { + popupOptions = options; + }} + /> + + , + { + wrapper: appRoot, + }, + ); + + await waitFor(() => expect(popupOptions).toBeDefined()); + + const mentionPopup = popupOptions?.find(({ trigger }) => trigger === '@'); + expect(mentionPopup).toBeDefined(); + + const items = await mentionPopup?.getItemsFromLocal?.(''); + + return items?.map(({ _id }) => _id) ?? []; +}; + +describe('ComposerPopupProvider', () => { + it('does not show @all or @here in autocomplete when user does not have permissions', async () => { + const itemIds = await renderProvider(); + + expect(itemIds).not.toContain('all'); + expect(itemIds).not.toContain('here'); + }); + + it('shows only @all when user has mention-all permission', async () => { + const itemIds = await renderProvider(['mention-all']); + + expect(itemIds).toContain('all'); + expect(itemIds).not.toContain('here'); + }); + + it('shows only @here when user has mention-here permission', async () => { + const itemIds = await renderProvider(['mention-here']); + + expect(itemIds).toContain('here'); + expect(itemIds).not.toContain('all'); + }); + + it('shows both @all and @here when user has both permissions', async () => { + const itemIds = await renderProvider(['mention-all', 'mention-here']); + + expect(itemIds).toContain('all'); + expect(itemIds).toContain('here'); + }); +}); diff --git a/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx b/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx index afce0c7cd751a..69de497fdcc07 100644 --- a/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx +++ b/apps/meteor/client/views/room/providers/ComposerPopupProvider.tsx @@ -94,6 +94,9 @@ const ComposerPopupProvider = ({ children, room }: ComposerPopupProviderProps) = const filterRegex = filter && new RegExp(escapeRegExp(filter), 'i'); const items: ComposerBoxPopupUserProps[] = []; + const canMentionAll = hasAtLeastOnePermission('mention-all', rid); + const canMentionHere = hasAtLeastOnePermission('mention-here', rid); + const roomMessageUsers = getLastRecentUsers(rid, uid!) .filter((u) => { if (!filterRegex) return true; @@ -106,7 +109,7 @@ const ComposerPopupProvider = ({ children, room }: ComposerPopupProviderProps) = suggestion: true, })); - if (!filterRegex || filterRegex.test('all')) { + if (canMentionAll && (!filterRegex || filterRegex.test('all'))) { items.push({ _id: 'all', username: 'all', @@ -116,7 +119,7 @@ const ComposerPopupProvider = ({ children, room }: ComposerPopupProviderProps) = }); } - if (!filterRegex || filterRegex.test('here')) { + if (canMentionHere && (!filterRegex || filterRegex.test('here'))) { items.push({ _id: 'here', username: 'here',