diff --git a/plugin-hrm-form/src/___tests__/maskIdentifiers/index.test.ts b/plugin-hrm-form/src/___tests__/maskIdentifiers/index.test.ts index 72ab68a3b9..2044270c95 100644 --- a/plugin-hrm-form/src/___tests__/maskIdentifiers/index.test.ts +++ b/plugin-hrm-form/src/___tests__/maskIdentifiers/index.test.ts @@ -19,15 +19,38 @@ import { Manager } from '@twilio/flex-ui'; import each from 'jest-each'; -import { maskConversationServiceUserNames } from '../../maskIdentifiers'; +import { maskConversationServiceUserNames, maskChannelStringsWithIdentifiers } from '../../maskIdentifiers'; import { getInitializedCan } from '../../permissions/rules'; import { PermissionActions } from '../../permissions/actions'; +import { lookupTranslation } from '../../translations'; jest.mock('../../permissions/rules', () => ({ getInitializedCan: jest.fn(), })); +jest.mock('../../translations', () => ({ + lookupTranslation: jest.fn(), +})); + +jest.mock('@twilio/flex-ui', () => ({ + Manager: { + getInstance: jest.fn(), + }, + NotificationIds: { + NewChatMessage: 'NewChatMessage', + }, + DefaultTaskChannels: { + ChatSms: { name: 'ChatSms' }, + }, + MessageList: { + Content: { remove: jest.fn() }, + }, +})); + +const mockLookupTranslation = lookupTranslation as jest.MockedFunction; + const mockGetInitializedCan = getInitializedCan as jest.MockedFunction; +const mockManagerGetInstance = Manager.getInstance as jest.MockedFunction; describe('maskConversationServiceUserNames', () => { let mockManager: any; @@ -391,3 +414,71 @@ describe('maskConversationServiceUserNames', () => { }); }); }); + +describe('maskChannelStringsWithIdentifiers', () => { + let mockCan: jest.Mock; + + const createChannelType = (name = 'default') => ({ + name, + templates: { + IncomingTaskCanvas: { firstLine: '' }, + TaskListItem: { firstLine: '', secondLine: '' }, + CallCanvas: { firstLine: '' }, + TaskCanvasHeader: { title: '' }, + Supervisor: { + TaskCanvasHeader: { title: '' }, + TaskOverviewCanvas: { firstLine: '' }, + }, + TaskCard: { firstLine: '' }, + }, + notifications: { + override: {} as Record void>, + }, + }); + + beforeEach(() => { + mockCan = jest.fn(); + mockGetInitializedCan.mockReturnValue(mockCan); + mockManagerGetInstance.mockReturnValue({ strings: { MaskIdentifiers: 'MASKED' } } as any); + mockLookupTranslation.mockReturnValue('Masked Title'); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('when VIEW_IDENTIFIERS permission is denied', () => { + beforeEach(() => { + mockCan.mockImplementation((action: string) => action !== PermissionActions.VIEW_IDENTIFIERS); + }); + + test('sets notification override for NewChatMessage', () => { + const channelType = createChannelType(); + maskChannelStringsWithIdentifiers(channelType as any); + expect(typeof channelType.notifications.override.NewChatMessage).toBe('function'); + }); + + test('notification override handler sets browser title using lookupTranslation', () => { + const channelType = createChannelType(); + maskChannelStringsWithIdentifiers(channelType as any); + + const notification = { options: { browser: { title: '' } } }; + channelType.notifications.override.NewChatMessage(notification); + + expect(mockLookupTranslation).toHaveBeenCalledWith('BrowserNotification-ChatMessage-MaskedTitle'); + expect(notification.options.browser.title).toBe('Masked Title'); + }); + }); + + describe('when VIEW_IDENTIFIERS permission is granted', () => { + beforeEach(() => { + mockCan.mockReturnValue(true); + }); + + test('does not set notification override', () => { + const channelType = createChannelType(); + maskChannelStringsWithIdentifiers(channelType as any); + expect(channelType.notifications.override.NewChatMessage).toBeUndefined(); + }); + }); +}); diff --git a/plugin-hrm-form/src/___tests__/translations/index.test.ts b/plugin-hrm-form/src/___tests__/translations/index.test.ts index 849363e8fc..5339f7b512 100644 --- a/plugin-hrm-form/src/___tests__/translations/index.test.ts +++ b/plugin-hrm-form/src/___tests__/translations/index.test.ts @@ -14,13 +14,20 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -import { loadTranslations, initLocalization } from '../../translations'; +import { Manager } from '@twilio/flex-ui'; + +import { loadTranslations, initLocalization, lookupTranslation } from '../../translations'; -// Mock translation files at the top level jest.mock('../../translations/en.json', () => ({}), { virtual: true }); jest.mock('../../translations/en-US.json', () => ({}), { virtual: true }); jest.mock('../../translations/en-GB.json', () => ({}), { virtual: true }); +jest.mock('@twilio/flex-ui', () => ({ + Manager: { + getInstance: jest.fn(), + }, +})); + const mockGetAseloFeatureFlags = jest.fn(); const mockGetHrmConfig = jest.fn(); const mockGetDefinitionVersions = jest.fn(); @@ -160,3 +167,40 @@ describe('Hierarchical Translations', () => { }); }); }); + +describe('lookupTranslation', () => { + const mockManagerGetInstance = Manager.getInstance as jest.MockedFunction; + + beforeAll(() => { + // eslint-disable-next-line global-require + (global as any).Handlebars = require('handlebars'); + }); + + afterAll(() => { + delete (global as any).Handlebars; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test('returns compiled string for a key that exists in Manager strings', () => { + mockManagerGetInstance.mockReturnValue({ strings: { MyKey: 'Hello World' } } as any); + expect(lookupTranslation('MyKey')).toBe('Hello World'); + }); + + test('falls back to using the code itself as template when key does not exist in Manager strings', () => { + mockManagerGetInstance.mockReturnValue({ strings: {} } as any); + expect(lookupTranslation('FallbackKey')).toBe('FallbackKey'); + }); + + test('passes parameters to the Handlebars template', () => { + mockManagerGetInstance.mockReturnValue({ strings: { Greeting: 'Hello {{name}}!' } } as any); + expect(lookupTranslation('Greeting', { name: 'World' })).toBe('Hello World!'); + }); + + test('uses empty parameters object by default', () => { + mockManagerGetInstance.mockReturnValue({ strings: { SimpleMsg: 'No params here' } } as any); + expect(lookupTranslation('SimpleMsg')).toBe('No params here'); + }); +});