Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion plugin-hrm-form/src/___tests__/maskIdentifiers/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof lookupTranslation>;

const mockGetInitializedCan = getInitializedCan as jest.MockedFunction<typeof getInitializedCan>;
const mockManagerGetInstance = Manager.getInstance as jest.MockedFunction<typeof Manager.getInstance>;

describe('maskConversationServiceUserNames', () => {
let mockManager: any;
Expand Down Expand Up @@ -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<string, (notification: any) => 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();
});
});
});
48 changes: 46 additions & 2 deletions plugin-hrm-form/src/___tests__/translations/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -160,3 +167,40 @@ describe('Hierarchical Translations', () => {
});
});
});

describe('lookupTranslation', () => {
const mockManagerGetInstance = Manager.getInstance as jest.MockedFunction<typeof Manager.getInstance>;

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');
});
});
Loading