Files
upstream-rocketchat/packages/apps/tests/server/managers/UIActionButtonManager.test.ts
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 2s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

312 lines
11 KiB
TypeScript

import * as assert from 'node:assert';
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
import type { IUIActionButtonDescriptor } from '@rocket.chat/apps-engine/definition/ui';
import { UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui';
import type { AppManager } from '../../../src/server/AppManager';
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
import type { AppActivationBridge, AppBridges } from '../../../src/server/bridges';
import { AppPermissionManager } from '../../../src/server/managers/AppPermissionManager';
import { UIActionButtonManager } from '../../../src/server/managers/UIActionButtonManager';
import { AppPermissions } from '../../../src/server/permissions/AppPermissions';
import { TestsAppBridges } from '../../test-data/bridges/appBridges';
describe('UIActionButtonManager', () => {
let mockBridges: TestsAppBridges;
let mockApp: ProxiedApp;
let mockApp2: ProxiedApp;
let mockManager: AppManager;
let mockActivationBridge: AppActivationBridge;
let hasPermissionSpy: ReturnType<typeof mock.method>;
let notifyAboutErrorSpy: ReturnType<typeof mock.method>;
let doActionsChangedSpy: ReturnType<typeof mock.method>;
beforeEach(() => {
mockBridges = new TestsAppBridges();
mockActivationBridge = mockBridges.getAppActivationBridge();
mockApp = {
getID() {
return 'testing-app';
},
getName() {
return 'Test App';
},
getStatus() {
return Promise.resolve(AppStatus.AUTO_ENABLED);
},
} as ProxiedApp;
mockApp2 = {
getID() {
return 'testing-app-2';
},
getName() {
return 'Test App 2';
},
getStatus() {
return Promise.resolve(AppStatus.AUTO_ENABLED);
},
} as ProxiedApp;
const bri = mockBridges;
const app = mockApp;
const app2 = mockApp2;
mockManager = {
getBridges(): AppBridges {
return bri;
},
getOneById: (appId: string): ProxiedApp | undefined => {
if (appId === 'testing-app') {
return app;
}
if (appId === 'testing-app-2') {
return app2;
}
return undefined;
},
} as AppManager;
notifyAboutErrorSpy = mock.method(AppPermissionManager, 'notifyAboutError');
hasPermissionSpy = mock.method(AppPermissionManager, 'hasPermission');
doActionsChangedSpy = mock.method(mockActivationBridge, 'doActionsChanged');
});
afterEach(() => {
mock.restoreAll();
});
it('basicUIActionButtonManager', () => {
assert.doesNotThrow(() => new UIActionButtonManager(mockManager));
const manager = new UIActionButtonManager(mockManager);
assert.strictEqual((manager as any).manager, mockManager);
assert.strictEqual((manager as any).activationBridge, mockActivationBridge);
assert.ok((manager as any).registeredActionButtons !== undefined);
assert.strictEqual((manager as any).registeredActionButtons.size, 0);
});
it('registerActionButtonWithPermission', () => {
hasPermissionSpy.mock.mockImplementation(() => true);
const manager = new UIActionButtonManager(mockManager);
const button: IUIActionButtonDescriptor = {
actionId: 'test-action',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label',
};
const result = manager.registerActionButton('testing-app', button);
assert.strictEqual(result, true);
assert.strictEqual(hasPermissionSpy.mock.calls.length, 1);
assert.deepStrictEqual(hasPermissionSpy.mock.calls[0].arguments, ['testing-app', AppPermissions.ui.registerButtons]);
assert.ok(doActionsChangedSpy.mock.calls.length > 0);
assert.strictEqual((manager as any).registeredActionButtons.size, 1);
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').size, 1);
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').get('test-action'), button);
});
it('registerActionButtonWithoutPermission', () => {
hasPermissionSpy.mock.mockImplementation(() => false);
notifyAboutErrorSpy.mock.mockImplementation(() => {});
const manager = new UIActionButtonManager(mockManager);
const button: IUIActionButtonDescriptor = {
actionId: 'test-action',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label',
};
const result = manager.registerActionButton('testing-app', button);
assert.strictEqual(result, false);
assert.strictEqual(hasPermissionSpy.mock.calls.length, 1);
assert.deepStrictEqual(hasPermissionSpy.mock.calls[0].arguments, ['testing-app', AppPermissions.ui.registerButtons]);
assert.ok(notifyAboutErrorSpy.mock.calls.length > 0);
assert.strictEqual(doActionsChangedSpy.mock.calls.length, 0);
assert.strictEqual((manager as any).registeredActionButtons.size, 0);
});
it('registerMultipleButtonsForSameApp', () => {
hasPermissionSpy.mock.mockImplementation(() => true);
const manager = new UIActionButtonManager(mockManager);
const button1: IUIActionButtonDescriptor = {
actionId: 'action-1',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label1',
};
const button2: IUIActionButtonDescriptor = {
actionId: 'action-2',
context: UIActionButtonContext.ROOM_ACTION,
labelI18n: 'test.label2',
};
manager.registerActionButton('testing-app', button1);
manager.registerActionButton('testing-app', button2);
assert.strictEqual((manager as any).registeredActionButtons.size, 1);
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').size, 2);
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').get('action-1'), button1);
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').get('action-2'), button2);
});
it('clearAppActionButtons', () => {
hasPermissionSpy.mock.mockImplementation(() => true);
const manager = new UIActionButtonManager(mockManager);
const button: IUIActionButtonDescriptor = {
actionId: 'test-action',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label',
};
manager.registerActionButton('testing-app', button);
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').size, 1);
manager.clearAppActionButtons('testing-app');
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').size, 0);
assert.strictEqual(doActionsChangedSpy.mock.calls.length, 2);
});
it('getAppActionButtons', () => {
hasPermissionSpy.mock.mockImplementation(() => true);
const manager = new UIActionButtonManager(mockManager);
const button: IUIActionButtonDescriptor = {
actionId: 'test-action',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label',
};
manager.registerActionButton('testing-app', button);
const buttons = manager.getAppActionButtons('testing-app');
assert.ok(buttons !== undefined);
assert.strictEqual(buttons?.size, 1);
assert.strictEqual(buttons?.get('test-action'), button);
const nonExistentButtons = manager.getAppActionButtons('non-existent');
assert.strictEqual(nonExistentButtons, undefined);
});
it('getAllActionButtonsFromEnabledApp', async () => {
hasPermissionSpy.mock.mockImplementation(() => true);
mock.method(mockApp, 'getStatus', () => Promise.resolve(AppStatus.AUTO_ENABLED));
const manager = new UIActionButtonManager(mockManager);
const button: IUIActionButtonDescriptor = {
actionId: 'test-action',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label',
};
manager.registerActionButton('testing-app', button);
const allButtons = await manager.getAllActionButtons();
assert.ok(allButtons !== undefined);
assert.strictEqual(allButtons.length, 1);
assert.strictEqual(allButtons[0].actionId, 'test-action');
assert.strictEqual(allButtons[0].appId, 'testing-app');
assert.strictEqual(allButtons[0].context, UIActionButtonContext.MESSAGE_ACTION);
assert.strictEqual(allButtons[0].labelI18n, 'test.label');
});
it('getAllActionButtonsFromDisabledApp', async () => {
hasPermissionSpy.mock.mockImplementation(() => true);
mock.method(mockApp, 'getStatus', () => Promise.resolve(AppStatus.DISABLED));
const manager = new UIActionButtonManager(mockManager);
const button: IUIActionButtonDescriptor = {
actionId: 'test-action',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label',
};
manager.registerActionButton('testing-app', button);
const allButtons = await manager.getAllActionButtons();
assert.ok(allButtons !== undefined);
assert.strictEqual(allButtons.length, 0);
});
it('getAllActionButtonsFromNonExistentApp', async () => {
hasPermissionSpy.mock.mockImplementation(() => true);
const manager = new UIActionButtonManager(mockManager);
const button: IUIActionButtonDescriptor = {
actionId: 'test-action',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label',
};
manager.registerActionButton('non-existent-app', button);
const allButtons = await manager.getAllActionButtons();
assert.ok(allButtons !== undefined);
assert.strictEqual(allButtons.length, 0);
});
it('getAllActionButtonsWithStatusError', async () => {
hasPermissionSpy.mock.mockImplementation(() => true);
mock.method(mockApp, 'getStatus', () => Promise.reject(new Error('Status error')));
const manager = new UIActionButtonManager(mockManager);
const button: IUIActionButtonDescriptor = {
actionId: 'test-action',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label',
};
manager.registerActionButton('testing-app', button);
const allButtons = await manager.getAllActionButtons();
assert.ok(allButtons !== undefined);
assert.strictEqual(allButtons.length, 0);
});
it('getAllActionButtonsFromMultipleApps', async () => {
hasPermissionSpy.mock.mockImplementation(() => true);
const button1: IUIActionButtonDescriptor = {
actionId: 'action-1',
context: UIActionButtonContext.MESSAGE_ACTION,
labelI18n: 'test.label1',
};
const button2: IUIActionButtonDescriptor = {
actionId: 'action-2',
context: UIActionButtonContext.ROOM_ACTION,
labelI18n: 'test.label2',
};
const manager = new UIActionButtonManager(mockManager);
manager.registerActionButton('testing-app', button1);
manager.registerActionButton('testing-app-2', button2);
const allButtons = await manager.getAllActionButtons();
assert.ok(allButtons !== undefined);
assert.strictEqual(allButtons.length, 2);
const app1Button = allButtons.find((b) => b.appId === 'testing-app');
const app2Button = allButtons.find((b) => b.appId === 'testing-app-2');
assert.ok(app1Button !== undefined);
assert.strictEqual(app1Button!.actionId, 'action-1');
assert.ok(app2Button !== undefined);
assert.strictEqual(app2Button!.actionId, 'action-2');
});
});