diff --git a/apps/meteor/.mocharc.client.js b/apps/meteor/.mocharc.client.js
index 7eff846f683..6e522ee6736 100644
--- a/apps/meteor/.mocharc.client.js
+++ b/apps/meteor/.mocharc.client.js
@@ -38,5 +38,5 @@ module.exports = {
'tests/unit/lib/**/*.tests.ts',
'tests/unit/client/**/*.test.ts',
],
- exclude: ['client/hooks/*.spec.{ts,tsx}'],
+ exclude: ['client/hooks/*.spec.{ts,tsx}', 'client/sidebar/header/actions/hooks/*.spec.{ts,tsx}'],
};
diff --git a/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts b/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts
deleted file mode 100644
index be38eaa6a22..00000000000
--- a/apps/meteor/app/ui-message/client/actionButtons/lib/applyButtonFilters.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Style disabled as having some arrow functions in one-line hurts readability */
-/* eslint-disable arrow-body-style */
-
-import { Meteor } from 'meteor/meteor';
-import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui';
-import { RoomTypeFilter } from '@rocket.chat/apps-engine/definition/ui';
-import type { IRoom } from '@rocket.chat/core-typings';
-import {
- isDirectMessageRoom,
- isMultipleDirectMessageRoom,
- isOmnichannelRoom,
- isPrivateDiscussion,
- isPrivateTeamRoom,
- isPublicDiscussion,
- isPublicTeamRoom,
-} from '@rocket.chat/core-typings';
-
-import { hasAtLeastOnePermission, hasPermission, hasRole, hasAnyRole } from '../../../../authorization/client';
-
-const applyAuthFilter = (button: IUIActionButton, room?: IRoom, ignoreSubscriptions = false): boolean => {
- const { hasAllPermissions, hasOnePermission, hasAllRoles, hasOneRole } = button.when || {};
-
- const userId = Meteor.userId();
-
- const hasAllPermissionsResult = hasAllPermissions ? hasPermission(hasAllPermissions) : true;
- const hasOnePermissionResult = hasOnePermission ? hasAtLeastOnePermission(hasOnePermission) : true;
- const hasAllRolesResult = hasAllRoles
- ? !!userId && hasAllRoles.every((role) => hasRole(userId, role, room?._id, ignoreSubscriptions))
- : true;
- const hasOneRoleResult = hasOneRole ? !!userId && hasAnyRole(userId, hasOneRole, room?._id, ignoreSubscriptions) : true;
-
- return hasAllPermissionsResult && hasOnePermissionResult && hasAllRolesResult && hasOneRoleResult;
-};
-
-const enumToFilter: { [k in RoomTypeFilter]: (room: IRoom) => boolean } = {
- [RoomTypeFilter.PUBLIC_CHANNEL]: (room) => room.t === 'c',
- [RoomTypeFilter.PRIVATE_CHANNEL]: (room) => room.t === 'p',
- [RoomTypeFilter.PUBLIC_TEAM]: isPublicTeamRoom,
- [RoomTypeFilter.PRIVATE_TEAM]: isPrivateTeamRoom,
- [RoomTypeFilter.PUBLIC_DISCUSSION]: isPublicDiscussion,
- [RoomTypeFilter.PRIVATE_DISCUSSION]: isPrivateDiscussion,
- [RoomTypeFilter.DIRECT]: isDirectMessageRoom,
- [RoomTypeFilter.DIRECT_MULTIPLE]: isMultipleDirectMessageRoom,
- [RoomTypeFilter.LIVE_CHAT]: isOmnichannelRoom,
-};
-
-const applyRoomFilter = (button: IUIActionButton, room: IRoom): boolean => {
- const { roomTypes } = button.when || {};
- return !roomTypes || roomTypes.some((filter): boolean => enumToFilter[filter]?.(room));
-};
-
-export const applyButtonFilters = (button: IUIActionButton, room?: IRoom): boolean => {
- return applyAuthFilter(button, room) && (!room || applyRoomFilter(button, room));
-};
-
-export const applyDropdownActionButtonFilters = (button: IUIActionButton): boolean => {
- return applyAuthFilter(button, undefined, true);
-};
diff --git a/apps/meteor/client/hooks/useAppActionButtons.ts b/apps/meteor/client/hooks/useAppActionButtons.ts
index 1f539c49b00..6d5aac3e92a 100644
--- a/apps/meteor/client/hooks/useAppActionButtons.ts
+++ b/apps/meteor/client/hooks/useAppActionButtons.ts
@@ -4,13 +4,13 @@ import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useRef, useMemo } from 'react';
-import { applyButtonFilters } from '../../app/ui-message/client/actionButtons/lib/applyButtonFilters';
import type { MessageActionConfig, MessageActionContext } from '../../app/ui-utils/client/lib/MessageAction';
import type { MessageBoxAction } from '../../app/ui-utils/client/lib/messageBox';
import { Utilities } from '../../ee/lib/misc/Utilities';
import type { GenericMenuItemProps } from '../components/GenericMenu/GenericMenuItem';
import { useRoom } from '../views/room/contexts/RoomContext';
import type { ToolboxAction } from '../views/room/lib/Toolbox';
+import { useApplyButtonFilters, useApplyButtonAuthFilter } from './useApplyButtonFilters';
import { useUiKitActionManager } from './useUiKitActionManager';
const getIdForActionButton = ({ appId, actionId }: IUIActionButton): string => `${appId}/${actionId}`;
@@ -49,13 +49,14 @@ export const useAppActionButtons = (context?: `${UIActionButtonContext}`) => {
export const useMessageboxAppsActionButtons = () => {
const result = useAppActionButtons('messageBoxAction');
const actionManager = useUiKitActionManager();
- const room = useRoom();
+
+ const applyButtonFilters = useApplyButtonFilters();
const data = useMemo(
() =>
result.data
?.filter((action) => {
- return applyButtonFilters(action, room);
+ return applyButtonFilters(action);
})
.map((action) => {
const item: MessageBoxAction = {
@@ -74,7 +75,7 @@ export const useMessageboxAppsActionButtons = () => {
return item;
}),
- [actionManager, result.data, room],
+ [actionManager, applyButtonFilters, result.data],
);
return {
...result,
@@ -86,6 +87,8 @@ export const useUserDropdownAppsActionButtons = () => {
const result = useAppActionButtons('userDropdownAction');
const actionManager = useUiKitActionManager();
+ const applyButtonFilters = useApplyButtonAuthFilter();
+
const data = useMemo(
() =>
result.data
@@ -106,7 +109,7 @@ export const useUserDropdownAppsActionButtons = () => {
},
};
}),
- [actionManager, result.data],
+ [actionManager, applyButtonFilters, result.data],
);
return {
...result,
@@ -117,6 +120,7 @@ export const useUserDropdownAppsActionButtons = () => {
export const useRoomActionAppsActionButtons = (context?: MessageActionContext) => {
const result = useAppActionButtons('roomAction');
const actionManager = useUiKitActionManager();
+ const applyButtonFilters = useApplyButtonFilters();
const room = useRoom();
const data = useMemo(
() =>
@@ -125,7 +129,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) =
if (context && ['group', 'channel', 'live', 'team', 'direct', 'direct_multiple'].includes(context)) {
return false;
}
- return applyButtonFilters(action, room);
+ return applyButtonFilters(action);
})
.map((action) => {
const item: [string, ToolboxAction] = [
@@ -149,7 +153,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) =
];
return item;
}),
- [actionManager, context, result.data, room],
+ [actionManager, applyButtonFilters, context, result.data, room._id],
);
return {
...result,
@@ -160,8 +164,7 @@ export const useRoomActionAppsActionButtons = (context?: MessageActionContext) =
export const useMessageActionAppsActionButtons = (context?: MessageActionContext) => {
const result = useAppActionButtons('messageAction');
const actionManager = useUiKitActionManager();
- const room = useRoom();
-
+ const applyButtonFilters = useApplyButtonFilters();
const data = useMemo(
() =>
result.data
@@ -172,7 +175,7 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext
) {
return false;
}
- return applyButtonFilters(action, room);
+ return applyButtonFilters(action);
})
.map((action) => {
const item: MessageActionConfig = {
@@ -192,7 +195,7 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext
return item;
}),
- [actionManager, context, result.data, room],
+ [actionManager, applyButtonFilters, context, result.data],
);
return {
...result,
diff --git a/apps/meteor/client/hooks/useApplyButtonFilters.ts b/apps/meteor/client/hooks/useApplyButtonFilters.ts
new file mode 100644
index 00000000000..742f33489de
--- /dev/null
+++ b/apps/meteor/client/hooks/useApplyButtonFilters.ts
@@ -0,0 +1,65 @@
+import type { IUIActionButton } from '@rocket.chat/apps-engine/definition/ui';
+import { RoomTypeFilter } from '@rocket.chat/apps-engine/definition/ui';
+import type { IRoom } from '@rocket.chat/core-typings';
+import {
+ isDirectMessageRoom,
+ isMultipleDirectMessageRoom,
+ isOmnichannelRoom,
+ isPrivateDiscussion,
+ isPrivateTeamRoom,
+ isPublicDiscussion,
+ isPublicTeamRoom,
+} from '@rocket.chat/core-typings';
+import { AuthorizationContext, useUserId } from '@rocket.chat/ui-contexts';
+import { useCallback, useContext } from 'react';
+
+import { useRoom } from '../views/room/contexts/RoomContext';
+
+const enumToFilter: { [k in RoomTypeFilter]: (room: IRoom) => boolean } = {
+ [RoomTypeFilter.PUBLIC_CHANNEL]: (room) => room.t === 'c',
+ [RoomTypeFilter.PRIVATE_CHANNEL]: (room) => room.t === 'p',
+ [RoomTypeFilter.PUBLIC_TEAM]: isPublicTeamRoom,
+ [RoomTypeFilter.PRIVATE_TEAM]: isPrivateTeamRoom,
+ [RoomTypeFilter.PUBLIC_DISCUSSION]: isPublicDiscussion,
+ [RoomTypeFilter.PRIVATE_DISCUSSION]: isPrivateDiscussion,
+ [RoomTypeFilter.DIRECT]: isDirectMessageRoom,
+ [RoomTypeFilter.DIRECT_MULTIPLE]: isMultipleDirectMessageRoom,
+ [RoomTypeFilter.LIVE_CHAT]: isOmnichannelRoom,
+};
+
+const applyRoomFilter = (button: IUIActionButton, room: IRoom): boolean => {
+ const { roomTypes } = button.when || {};
+ return !roomTypes || roomTypes.some((filter): boolean => enumToFilter[filter]?.(room));
+};
+
+export const useApplyButtonFilters = (): ((button: IUIActionButton) => boolean) => {
+ const room = useRoom();
+ if (!room) {
+ throw new Error('useApplyButtonFilters must be used inside a room context');
+ }
+ const applyAuthFilter = useApplyButtonAuthFilter();
+ return useCallback(
+ (button: IUIActionButton) => applyAuthFilter(button) && (!room || applyRoomFilter(button, room)),
+ [applyAuthFilter, room],
+ );
+};
+
+export const useApplyButtonAuthFilter = (): ((button: IUIActionButton) => boolean) => {
+ const uid = useUserId();
+
+ const { queryAllPermissions, queryAtLeastOnePermission, queryRole } = useContext(AuthorizationContext);
+
+ return useCallback(
+ (button: IUIActionButton, room?: IRoom) => {
+ const { hasAllPermissions, hasOnePermission, hasAllRoles, hasOneRole } = button.when || {};
+
+ const hasAllPermissionsResult = hasAllPermissions ? queryAllPermissions(hasAllPermissions)[1]() : true;
+ const hasOnePermissionResult = hasOnePermission ? queryAtLeastOnePermission(hasOnePermission)[1]() : true;
+ const hasAllRolesResult = hasAllRoles ? !!uid && hasAllRoles.every((role) => queryRole(role, room?._id)) : true;
+ const hasOneRoleResult = hasOneRole ? !!uid && hasOneRole.some((role) => queryRole(role, room?._id)[1]()) : true;
+
+ return hasAllPermissionsResult && hasOnePermissionResult && hasAllRolesResult && hasOneRoleResult;
+ },
+ [queryAllPermissions, queryAtLeastOnePermission, queryRole, uid],
+ );
+};
diff --git a/apps/meteor/client/providers/AuthorizationProvider.tsx b/apps/meteor/client/providers/AuthorizationProvider.tsx
index 8fb0e69d12a..64d936b5cd6 100644
--- a/apps/meteor/client/providers/AuthorizationProvider.tsx
+++ b/apps/meteor/client/providers/AuthorizationProvider.tsx
@@ -20,7 +20,10 @@ const contextValue = {
queryPermission: createReactiveSubscriptionFactory((permission, scope, scopeRoles) => hasPermission(permission, scope, scopeRoles)),
queryAtLeastOnePermission: createReactiveSubscriptionFactory((permissions, scope) => hasAtLeastOnePermission(permissions, scope)),
queryAllPermissions: createReactiveSubscriptionFactory((permissions, scope) => hasAllPermission(permissions, scope)),
- queryRole: createReactiveSubscriptionFactory((role) => !!Meteor.userId() && hasRole(Meteor.userId() as string, role)),
+ queryRole: createReactiveSubscriptionFactory(
+ (role, scope?, ignoreSubscriptions = false) =>
+ !!Meteor.userId() && hasRole(Meteor.userId() as string, role, scope, ignoreSubscriptions),
+ ),
roleStore: new RoleStore(),
};
diff --git a/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx
new file mode 100644
index 00000000000..a520444389a
--- /dev/null
+++ b/apps/meteor/client/sidebar/header/actions/hooks/useAppsItems.spec.tsx
@@ -0,0 +1,488 @@
+/* eslint-disable react/no-multi-comp */
+import { MockedAuthorizationContext } from '@rocket.chat/mock-providers/src/MockedAuthorizationContext';
+import { MockedServerContext } from '@rocket.chat/mock-providers/src/MockedServerContext';
+import { MockedSettingsContext } from '@rocket.chat/mock-providers/src/MockedSettingsContext';
+import { MockedUserContext } from '@rocket.chat/mock-providers/src/MockedUserContext';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { renderHook } from '@testing-library/react-hooks';
+import React from 'react';
+
+import { ActionManagerContext } from '../../../../contexts/ActionManagerContext';
+import { useAppsItems } from './useAppsItems';
+
+it('should return and empty array if the user does not have `manage-apps` and `access-marketplace` permission', () => {
+ const { result } = renderHook(
+ () => {
+ return useAppsItems();
+ },
+ {
+ wrapper: ({ children }) => (
+
+ {
+ if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') {
+ return [] as any;
+ }
+ }}
+ >
+
+
+ {children}
+
+
+
+
+ ),
+ },
+ );
+
+ expect(result.all[0]).toEqual([]);
+});
+
+it('should return `marketplace` and `installed` items if the user has `access-marketplace` permission', () => {
+ const { result } = renderHook(
+ () => {
+ return useAppsItems();
+ },
+ {
+ wrapper: ({ children }) => (
+
+ {
+ if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') {
+ return [] as any;
+ }
+ }}
+ >
+
+
+
+ {children}
+
+
+
+
+
+ ),
+ },
+ );
+
+ expect(result.current[0]).toEqual(
+ expect.objectContaining({
+ id: 'marketplace',
+ }),
+ );
+ expect(result.current[1]).toEqual(
+ expect.objectContaining({
+ id: 'installed',
+ }),
+ );
+});
+
+it('should return `marketplace` and `installed` items if the user has `manage-apps` permission', () => {
+ const { result } = renderHook(
+ () => {
+ return useAppsItems();
+ },
+ {
+ wrapper: ({ children }) => (
+
+ {
+ if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') {
+ return {
+ data: {
+ totalSeen: 0,
+ totalUnseen: 1,
+ },
+ } as any;
+ }
+ if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') {
+ return [] as any;
+ }
+
+ throw new Error('Method not mocked');
+ }}
+ >
+
+
+
+ {children}
+
+
+
+
+
+ ),
+ },
+ );
+
+ expect(result.current[0]).toEqual(
+ expect.objectContaining({
+ id: 'marketplace',
+ }),
+ );
+ expect(result.current[1]).toEqual(
+ expect.objectContaining({
+ id: 'installed',
+ }),
+ );
+
+ expect(result.current[2]).toEqual(
+ expect.objectContaining({
+ id: 'requested-apps',
+ }),
+ );
+});
+
+it('should return one action from the server with no conditions', async () => {
+ const { result, waitForValueToChange } = renderHook(
+ () => {
+ return useAppsItems();
+ },
+ {
+ wrapper: ({ children }) => (
+
+ {
+ if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') {
+ return {
+ data: {
+ totalSeen: 0,
+ totalUnseen: 1,
+ },
+ } as any;
+ }
+ if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') {
+ return [
+ {
+ appId: 'APP_ID',
+ actionId: 'ACTION_ID',
+ labelI18n: 'LABEL_I18N',
+ context: 'userDropdownAction',
+ },
+ ] as any;
+ }
+
+ throw new Error('Method not mocked');
+ }}
+ >
+
+
+
+ {children}
+
+
+
+
+
+ ),
+ },
+ );
+
+ expect(result.current[0]).toEqual(
+ expect.objectContaining({
+ id: 'marketplace',
+ }),
+ );
+ expect(result.current[1]).toEqual(
+ expect.objectContaining({
+ id: 'installed',
+ }),
+ );
+
+ await waitForValueToChange(() => result.current[3]);
+
+ expect(result.current[3]).toEqual(
+ expect.objectContaining({
+ id: 'APP_ID_ACTION_ID',
+ }),
+ );
+});
+
+describe('User Dropdown actions with role conditions', () => {
+ it('should return the action if the user has admin role', async () => {
+ const { result, waitForValueToChange } = renderHook(
+ () => {
+ return useAppsItems();
+ },
+ {
+ wrapper: ({ children }) => (
+
+ {
+ if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') {
+ return {
+ data: {
+ totalSeen: 0,
+ totalUnseen: 1,
+ },
+ } as any;
+ }
+ if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') {
+ return [
+ {
+ appId: 'APP_ID',
+ actionId: 'ACTION_ID',
+ labelI18n: 'LABEL_I18N',
+ context: 'userDropdownAction',
+ when: {
+ hasOneRole: ['admin'],
+ },
+ },
+ ] as any;
+ }
+
+ throw new Error('Method not mocked');
+ }}
+ >
+
+
+
+ {children}
+
+
+
+
+
+ ),
+ },
+ );
+
+ await waitForValueToChange(() => {
+ return queryClient.isFetching();
+ });
+
+ expect(result.current[0]).toEqual(
+ expect.objectContaining({
+ id: 'marketplace',
+ }),
+ );
+ expect(result.current[1]).toEqual(
+ expect.objectContaining({
+ id: 'installed',
+ }),
+ );
+
+ expect(result.current[3]).toEqual(
+ expect.objectContaining({
+ id: 'APP_ID_ACTION_ID',
+ }),
+ );
+ });
+
+ it('should return filter the action if the user doesn`t have admin role', async () => {
+ const { result, waitForValueToChange } = renderHook(
+ () => {
+ return useAppsItems();
+ },
+ {
+ wrapper: ({ children }) => (
+
+ {
+ if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') {
+ return {
+ data: {
+ totalSeen: 0,
+ totalUnseen: 1,
+ },
+ } as any;
+ }
+ if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') {
+ return [
+ {
+ appId: 'APP_ID',
+ actionId: 'ACTION_ID',
+ labelI18n: 'LABEL_I18N',
+ context: 'userDropdownAction',
+ when: {
+ hasOneRole: ['admin'],
+ },
+ },
+ ] as any;
+ }
+
+ throw new Error('Method not mocked');
+ }}
+ >
+
+
+
+ {children}
+
+
+
+
+
+ ),
+ },
+ );
+
+ await waitForValueToChange(() => {
+ return queryClient.isFetching();
+ });
+
+ expect(result.current[0]).toEqual(
+ expect.objectContaining({
+ id: 'marketplace',
+ }),
+ );
+ expect(result.current[1]).toEqual(
+ expect.objectContaining({
+ id: 'installed',
+ }),
+ );
+
+ expect(result.current[2]).toEqual(
+ expect.objectContaining({
+ id: 'requested-apps',
+ }),
+ );
+
+ expect(result.current[3]).toEqual(undefined);
+ });
+});
+
+describe('User Dropdown actions with permission conditions', () => {
+ it('should return the action if the user has manage-apps permission', async () => {
+ const { result, waitForValueToChange } = renderHook(
+ () => {
+ return useAppsItems();
+ },
+ {
+ wrapper: ({ children }) => (
+
+ {
+ if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') {
+ return {
+ data: {
+ totalSeen: 0,
+ totalUnseen: 1,
+ },
+ } as any;
+ }
+ if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') {
+ return [
+ {
+ appId: 'APP_ID',
+ actionId: 'ACTION_ID',
+ labelI18n: 'LABEL_I18N',
+ context: 'userDropdownAction',
+ when: {
+ hasOnePermission: ['manage-apps'],
+ },
+ },
+ ] as any;
+ }
+
+ throw new Error('Method not mocked');
+ }}
+ >
+
+
+
+ {children}
+
+
+
+
+
+ ),
+ },
+ );
+
+ await waitForValueToChange(() => {
+ return queryClient.isFetching();
+ });
+
+ expect(result.current[0]).toEqual(
+ expect.objectContaining({
+ id: 'marketplace',
+ }),
+ );
+ expect(result.current[1]).toEqual(
+ expect.objectContaining({
+ id: 'installed',
+ }),
+ );
+
+ expect(result.current[3]).toEqual(
+ expect.objectContaining({
+ id: 'APP_ID_ACTION_ID',
+ }),
+ );
+ });
+
+ it('should return filter the action if the user doesn`t have `any` permission', async () => {
+ const { result, waitForValueToChange } = renderHook(
+ () => {
+ return useAppsItems();
+ },
+ {
+ wrapper: ({ children }) => (
+
+ {
+ if (args.method === 'GET' && args.pathPattern === '/apps/app-request/stats') {
+ return {
+ data: {
+ totalSeen: 0,
+ totalUnseen: 1,
+ },
+ } as any;
+ }
+ if (args.method === 'GET' && args.pathPattern === '/apps/actionButtons') {
+ return [
+ {
+ appId: 'APP_ID',
+ actionId: 'ACTION_ID',
+ labelI18n: 'LABEL_I18N',
+ context: 'userDropdownAction',
+ when: {
+ hasOnePermission: ['any'],
+ },
+ },
+ ] as any;
+ }
+
+ throw new Error('Method not mocked');
+ }}
+ >
+
+
+
+ {children}
+
+
+
+
+
+ ),
+ },
+ );
+
+ await waitForValueToChange(() => {
+ return queryClient.isFetching();
+ });
+
+ expect(result.current[3]).toEqual(undefined);
+ });
+});
+
+export const MockedUiKitActionManager = ({ children }: { children: React.ReactNode }) => {
+ return {children};
+};
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ // ✅ turns retries off
+ retry: false,
+ },
+ },
+});
+afterEach(() => {
+ queryClient.clear();
+});
diff --git a/apps/meteor/jest.client.config.ts b/apps/meteor/jest.client.config.ts
index db3a03ecf03..bcd9f8f5432 100644
--- a/apps/meteor/jest.client.config.ts
+++ b/apps/meteor/jest.client.config.ts
@@ -3,7 +3,11 @@ export default {
testEnvironment: 'jsdom',
modulePathIgnorePatterns: ['/dist/'],
- testMatch: ['/client/hooks/**.spec.[jt]s?(x)', '/client/components/**.spec.[jt]s?(x)'],
+ testMatch: [
+ '/client/hooks/**.spec.[jt]s?(x)',
+ '/client/components/**.spec.[jt]s?(x)',
+ '/client/sidebar/header/actions/hooks/**/**.spec.[jt]s?(x)',
+ ],
transform: {
'^.+\\.(t|j)sx?$': '@swc/jest',
},
diff --git a/packages/mock-providers/src/MockedAuthorizationContext.tsx b/packages/mock-providers/src/MockedAuthorizationContext.tsx
index 18f791f1dd9..4d2a5c05a47 100644
--- a/packages/mock-providers/src/MockedAuthorizationContext.tsx
+++ b/packages/mock-providers/src/MockedAuthorizationContext.tsx
@@ -1,14 +1,25 @@
import React from 'react';
import { AuthorizationContext } from '@rocket.chat/ui-contexts';
-export const MockedAuthorizationContext = ({ permissions = [], children }: { permissions: string[]; children: React.ReactNode }) => {
+export const MockedAuthorizationContext = ({
+ permissions = [],
+ roles = [],
+ children,
+}: {
+ permissions: string[];
+ roles?: string[];
+ children: React.ReactNode;
+}) => {
return (
[() => (): void => undefined, (): boolean => permissions.includes(id)],
- queryAtLeastOnePermission: () => [() => (): void => undefined, (): boolean => false],
- queryAllPermissions: () => [() => (): void => undefined, (): boolean => false],
- queryRole: () => [() => (): void => undefined, (): boolean => false],
+ queryAtLeastOnePermission: (ids: string[]) => [
+ () => (): void => undefined,
+ (): boolean => ids.some((id) => permissions.includes(id)),
+ ],
+ queryAllPermissions: (ids: string[]) => [() => (): void => undefined, (): boolean => ids.every((id) => permissions.includes(id))],
+ queryRole: (id: string) => [() => (): void => undefined, (): boolean => roles.includes(id)],
roleStore: {
roles: {},
emit: (): void => undefined,
diff --git a/packages/mock-providers/src/MockedServerContext.tsx b/packages/mock-providers/src/MockedServerContext.tsx
index f3c13004ed7..6b23664f816 100644
--- a/packages/mock-providers/src/MockedServerContext.tsx
+++ b/packages/mock-providers/src/MockedServerContext.tsx
@@ -33,6 +33,7 @@ export const MockedServerContext = ({
}) => {
return handleRequest(args);
},
+ getStream: () => () => undefined,
} as any
}
>
diff --git a/packages/ui-contexts/src/AuthorizationContext.ts b/packages/ui-contexts/src/AuthorizationContext.ts
index a321c408672..d077568e772 100644
--- a/packages/ui-contexts/src/AuthorizationContext.ts
+++ b/packages/ui-contexts/src/AuthorizationContext.ts
@@ -1,4 +1,4 @@
-import type { IRole } from '@rocket.chat/core-typings';
+import type { IRole, IRoom } from '@rocket.chat/core-typings';
import type { IEmitter } from '@rocket.chat/emitter';
import { createContext } from 'react';
import type { ObjectId } from 'mongodb';
@@ -27,7 +27,11 @@ export type AuthorizationContextValue = {
scope?: string | ObjectId,
scopedRoles?: IRole['_id'][],
): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean];
- queryRole(role: string | ObjectId): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean];
+ queryRole(
+ role: string | ObjectId,
+ scope?: IRoom['_id'],
+ ignoreSubscriptions?: boolean,
+ ): [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => boolean];
roleStore: RoleStore;
};