This commit is contained in:
Daniil Deli 2020-10-21 17:15:54 +03:00 коммит произвёл GitHub
Родитель b728f68f1f
Коммит 1e5422e087
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 1211 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,120 @@
import createEgressMessageActivityMiddleware from './../../../../src/ic3/enhancers/egress/createEgressFileAttachmentMiddleware';
import { StateKey } from './../../../../src/types/ic3/IC3AdapterState';
import { TelemetryEvents } from './../../../../src/types/ic3/TelemetryEvents';
import { ActivityType } from './../../../../src/types/DirectLineTypes';
describe('createEgressFileAttachmentMiddleware test', () => {
let globalMicrosoftBefore;
let fetchBefore;
beforeAll(() => {
spyOn(Date.prototype, 'toISOString').and.returnValue('dateString');
globalMicrosoftBefore = global.Microsoft;
fetchBefore = global.fetch;
global.Microsoft = {
CRM: {
Omnichannel: {
IC3Client: {
Model: {
LogLevel: { ERROR: 'ERROR' }
}
}
},
}
}
});
afterAll(() => {
global.Microsoft = globalMicrosoftBefore;
global.fetch = fetchBefore;
});
test('should return next', async () => {
const activity = { type: ActivityType.Message }
const next = 'next'
const result = await createEgressMessageActivityMiddleware()({getState: () => {}})(() => next)(activity)
expect(result).toBe(next);
});
test('should throw error', async () => {
const mockLogClientSdkTelemetryEvent = jest.fn();
const activity = {
type: ActivityType.Message,
attachments: [{}]
}
const getState = (key) => key === StateKey.Logger ? { logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent } : null;
try {
await createEgressMessageActivityMiddleware()({getState})()(activity);
} catch(e) {
expect(e).toEqual(new Error('IC3: Failed to egress without an active conversation.'));
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('ERROR', {
Event: TelemetryEvents.CONVERSATION_NOT_FOUND,
Description: `Adapter: Failed to egress without an active conversation.`
});
}
});
test('should call next with correct individualActivity', async () => {
global.fetch = () => ({
ok: true,
blob: () => 'resBlob'
});
const next = jest.fn();
const activity = {
type: ActivityType.Message,
attachments: [
{ contentUrl: 'contentUrl1', name: 'attachmentName1', contentType: 'type1' },
{ contentUrl: 'contentUrl2', name: 'attachmentName2', contentType: 'type2' }
]
}
const conversation = { uploadFile: () => 'file uploaded' }
const getState = () => conversation;
await createEgressMessageActivityMiddleware()({ getState })(next)(activity);
const expectedIndividualActivity = {
attachments: [
{
blob: 'resBlob',
contentType: activity.attachments[0].contentType,
contentUrl: activity.attachments[0].contentUrl,
name: activity.attachments[0].name
},
{
blob: 'resBlob',
contentType: activity.attachments[1].contentType,
contentUrl: activity.attachments[1].contentUrl,
name: activity.attachments[1].name
}
],
channelData: {
middlewareData: {
[activity.attachments[0].name]: activity.attachments[0].contentUrl,
[activity.attachments[1].name]: activity.attachments[1].contentUrl
},
uploadedFileMetadata: 'file uploaded'
},
timestamp: 'dateString',
type: ActivityType.Message
};
expect(next).toHaveBeenCalledWith(expectedIndividualActivity)
});
test('should throw error on failed attachment fetch', async () => {
const mockLogClientSdkTelemetryEvent = jest.fn();
global.fetch = () => ({ ok: false });
const activity = {
type: ActivityType.Message,
attachments: [{ contentUrl: 'contentUrl1', name: 'attachmentName1', contentType: 'type1' }]
}
const getState = () => ({logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent});
try {
await createEgressMessageActivityMiddleware()({ getState })(() => {})(activity);
} catch(e) {
expect(e).toEqual(new Error('IC3: Failed to fetch attachment to send.'));
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('ERROR', {
Event: TelemetryEvents.FETCH_ATTACHMENT_FAILED,
Description: `Adapter: Failed to fetch attachment to send.`
});
}
});
});

Просмотреть файл

@ -0,0 +1,202 @@
import createEgressMessageActivityMiddleware from './../../../../src/ic3/enhancers/egress/createEgressMessageActivityMiddleware';
import { ActivityType } from './../../../../src/types/DirectLineTypes';
import { TelemetryEvents } from './../../../../src/types/ic3/TelemetryEvents';
import { StateKey } from './../../../../src/types/ic3/IC3AdapterState';
describe('test createEgressMessageActivityMiddleware', () => {
let globalMicrosoftBefore;
const testDateNowValue = 123
const textContentType = 'Text';
const testActivityId = 'testActivityId';
const userPersonType = 0;
const userMessageType = 0;
const logClientSdkTelemetryEventSpy = jest.fn();
const sendMessageMock = jest.fn();
const sendFileMessageMock = jest.fn();
const next = () => 'next';
const mockGetState = (key) => {
switch(key) {
case StateKey.Conversation: {
return {
sendMessage: sendMessageMock,
sendFileMessage: sendFileMessageMock
}
}
case StateKey.UserDisplayName: return StateKey.UserDisplayName;
case StateKey.Logger: return { logClientSdkTelemetryEvent: logClientSdkTelemetryEventSpy};
default: return null
}
}
beforeAll(() => {
spyOn(Date, 'now').and.returnValue(testDateNowValue);
globalMicrosoftBefore = global.Microsoft;
global.Microsoft = {
CRM: {
Omnichannel: {
IC3Client: {
Model: {
LogLevel: { ERROR: 'ERROR', DEBUG: 'DEBUG' },
MessageContentType: { Text: textContentType },
MessageType: { UserMessage: userMessageType },
PersonType: { User: userPersonType },
DeliveryMode: { Bridged: 'Bridged' }
}
}
},
}
}
});
afterAll(() => {
global.Microsoft = globalMicrosoftBefore;
});
it('should return next', async () => {
const activity = { type: ActivityType.Typing };
const result = await createEgressMessageActivityMiddleware()({getState: () => ({})})(next)(activity);
expect(result).toEqual(next());
});
it('should throw an error when no conversation', async () => {
const activity = { type: ActivityType.Message };
try {
await createEgressMessageActivityMiddleware()({getState: () => null})(next)(activity);
} catch(e) {
expect(e).toEqual(new Error('IC3: Failed to egress without an active conversation.'));
}
});
it('should send correct text message', async () => {
const activity = {
type: ActivityType.Message,
channelData: { deliveryMode: 'testMode' },
from: { id: 'testFromId' },
timestamp: 123
};
await createEgressMessageActivityMiddleware()({getState: mockGetState})(next)(activity);
const expectedMessage = {
clientmessageid: `${testDateNowValue}`,
content: '',
contentType: textContentType,
deliveryMode: 'testMode',
messageType: 0,
properties: activity.channelData,
sender: { displayName: StateKey.UserDisplayName, id: activity.from.id, type: 0 },
tags: [],
timestamp: new Date(activity.timestamp)
}
expect(logClientSdkTelemetryEventSpy).toHaveBeenCalledWith(
'DEBUG',
{
Event: TelemetryEvents.SEND_MESSAGE_SUCCESS,
Description: `Adapter: Successfully sent a message with clientmessageid ${testDateNowValue}`
}
);
expect(sendMessageMock).toHaveBeenCalledWith(expectedMessage);
});
it('should send correct text message without tags', async () => {
const activity = {
type: ActivityType.Message,
channelData: { clientActivityID: testActivityId },
from: { id: 'testFromId' },
timestamp: 123,
value: {}
};
await createEgressMessageActivityMiddleware()({getState: mockGetState})(next)(activity);
const expectedMessage = {
clientmessageid: `${testDateNowValue}`,
content: JSON.stringify(activity.value),
contentType: textContentType,
deliveryMode: 'Bridged',
messageType: 0,
properties: { deliveryMode: 'Bridged' },
sender: { displayName: StateKey.UserDisplayName, id: activity.from.id, type: 0 },
tags: ["client_activity_id:testActivityId"],
timestamp: new Date(activity.timestamp)
}
expect(logClientSdkTelemetryEventSpy).toHaveBeenCalledWith(
'DEBUG',
{
Event: TelemetryEvents.SEND_MESSAGE_SUCCESS,
Description: `Adapter: Successfully sent a message with clientmessageid ${testDateNowValue}`
}
);
expect(sendMessageMock).toHaveBeenCalledWith(expectedMessage);
});
it('should send correct text message with tags', async () => {
const clientActivityId = 'client_activity_id:1'
const activity = {
type: ActivityType.Message,
channelData: {
clientActivityID: testActivityId,
tags: [clientActivityId, 'testTag']
},
from: { id: 'testFromId' },
timestamp: 123,
value: {}
};
await createEgressMessageActivityMiddleware()({getState: mockGetState})(next)(activity);
const expectedMessage = {
clientmessageid: `${testDateNowValue}`,
content: JSON.stringify(activity.value),
contentType: textContentType,
deliveryMode: 'Bridged',
messageType: 0,
properties: { deliveryMode: 'Bridged' },
sender: { displayName: StateKey.UserDisplayName, id: activity.from.id, type: 0 },
tags: ["client_activity_id:testActivityId", 'testTag'],
timestamp: new Date(activity.timestamp)
}
expect(logClientSdkTelemetryEventSpy).toHaveBeenCalledWith(
'DEBUG',
{
Event: TelemetryEvents.SEND_MESSAGE_SUCCESS,
Description: `Adapter: Successfully sent a message with clientmessageid ${testDateNowValue}`
}
);
expect(sendMessageMock).toHaveBeenCalledWith(expectedMessage);
});
it('should send correct file message', async () => {
const clientActivityId = 'client_activity_id:1'
const activity = {
type: ActivityType.Message,
channelData: {
clientActivityID: testActivityId,
tags: [clientActivityId, 'testTag'],
uploadedFileMetadata: 'fileMetaData'
},
from: { id: 'testFromId' },
timestamp: 123,
value: {},
previousClientActivityID: 'previousActivityId'
};
await createEgressMessageActivityMiddleware()({getState: mockGetState})(next)(activity);
const expectedMessage = {
clientmessageid: `${testDateNowValue}`,
content: JSON.stringify(activity.value),
contentType: textContentType,
deliveryMode: 'Bridged',
messageType: 0,
properties: { deliveryMode: 'Bridged' },
sender: { displayName: StateKey.UserDisplayName, id: activity.from.id, type: 0 },
tags: ["client_activity_id:testActivityId", 'testTag', "previousClientActivityID:previousActivityId"],
timestamp: new Date(activity.timestamp)
}
expect(logClientSdkTelemetryEventSpy).toHaveBeenCalledWith(
'DEBUG',
{
Event: TelemetryEvents.SEND_FILE_SUCCESS,
Description: `Adapter: Successfully sent a file with clientmessageid ${testDateNowValue}`
}
);
expect(sendFileMessageMock).toHaveBeenCalledWith(activity.channelData.uploadedFileMetadata, expectedMessage);
});
});

Просмотреть файл

@ -0,0 +1,78 @@
import createEgressTypingActivityMiddleware from './../../../../src/ic3/enhancers/egress/createEgressTypingActivityMiddleware';
import { ActivityType } from './../../../../src/types/DirectLineTypes';
import { TelemetryEvents } from './../../../../src/types/ic3/TelemetryEvents';
import { StateKey } from './../../../../src/types/ic3/IC3AdapterState';
describe('createEgressTypingActivityMiddleware test', () => {
let globalMicrosoftBefore;
const logClientSdkTelemetryEventSpy = jest.fn();
const indicateTypingStatusMock = jest.fn();
const sendMessageToBotMock = jest.fn();
const next = () => 'next';
const mockGetState = (key) => {
switch(key) {
case StateKey.Conversation: {
return {
indicateTypingStatus: indicateTypingStatusMock,
sendMessageToBot: sendMessageToBotMock
}
}
case StateKey.UserDisplayName: return StateKey.UserDisplayName;
case StateKey.BotId: return StateKey.BotId
case StateKey.Logger: return { logClientSdkTelemetryEvent: logClientSdkTelemetryEventSpy};
default: return null
}
}
beforeAll(() => {
globalMicrosoftBefore = global.Microsoft;
global.Microsoft = {
CRM: {
Omnichannel: {
IC3Client: {
Model: {
LogLevel: { DEBUG: 'DEBUG' },
TypingStatus: { Typing: 'Typing' }
}
}
},
}
}
});
afterAll(() => {
global.Microsoft = globalMicrosoftBefore;
});
test('should return next when activity type is not typing', () => {
const result = createEgressTypingActivityMiddleware()({getState: mockGetState})(next)({});
expect(result).toEqual(next());
});
test('should throw error when no conversationg', () => {
const activity = { type: ActivityType.Typing }
try {
createEgressTypingActivityMiddleware()({getState: () => null})(next)(activity);
} catch(e) {
expect(e).toEqual(new Error('IC3: Failed to egress without an active conversation.'));
}
});
test('should indicate typing status, send message to bot and log correct event', () => {
const activity = {
type: ActivityType.Typing,
channelData: { tags: ['tag'] }
}
createEgressTypingActivityMiddleware()({getState: mockGetState})(next)(activity);
expect(indicateTypingStatusMock).toHaveBeenCalledWith('Typing', {
imdisplayname: StateKey.UserDisplayName
});
expect(sendMessageToBotMock).toHaveBeenCalledWith(StateKey.BotId, {
payload: '{"isTyping":true}'
});
expect(logClientSdkTelemetryEventSpy).toHaveBeenCalledWith('DEBUG', {
Event: TelemetryEvents.SEND_TYPING_SUCCESS,
Description: `Adapter: Successfully sent a typing indication`
});
});
});

Просмотреть файл

@ -0,0 +1,18 @@
import createEgressEnhancer from './../../../../src/ic3/enhancers/egress/index';
import * as applyEgressMiddleware from './../../../../src/applyEgressMiddleware';
import * as createEgressFileAttachmentMiddleware from './../../../../src/ic3/enhancers/egress/createEgressFileAttachmentMiddleware';
import * as createEgressMessageActivityMiddleware from './../../../../src/ic3/enhancers/egress/createEgressMessageActivityMiddleware';
import * as createEgressTypingActivityMiddleware from './../../../../src/ic3/enhancers/egress/createEgressTypingActivityMiddleware';
describe('createEgressEnhancer test', () => {
test('should call applyEgressMiddleware with correct params', () => {
spyOn(applyEgressMiddleware, 'default');
createEgressFileAttachmentMiddleware.default = jest.fn().mockReturnValue('mock1');
createEgressMessageActivityMiddleware.default = jest.fn().mockReturnValue('mock2');
createEgressTypingActivityMiddleware.default = jest.fn().mockReturnValue('mock2');
createEgressEnhancer();
expect(applyEgressMiddleware.default).toHaveBeenCalledWith('mock1', 'mock2', 'mock2');
});
});

Просмотреть файл

@ -0,0 +1,161 @@
import createExtractAdaptiveCardMiddleware from './../../../../src/ic3/enhancers/ingress/createExtractAdaptiveCardMiddleware';
import { ActivityType } from './../../../../src/types/DirectLineTypes';
import { TelemetryEvents } from './../../../../src/types/ic3/TelemetryEvents';
describe('createExtractAdaptiveCardMiddleware test', () => {
let globalMicrosoftBefore;
let globalDecodeURIComponentBefore;
const mockLogClientSdkTelemetryEvent = jest.fn();
const next = 'next';
beforeAll(() => {
globalMicrosoftBefore = global.Microsoft;
globalDecodeURIComponentBefore = global.decodeURIComponent;
global.Microsoft = {
CRM: {
Omnichannel: {
IC3Client: {
Model: {
LogLevel: { WARN: 'WARN' }
}
}
},
}
}
});
afterAll(() => {
global.Microsoft = globalMicrosoftBefore;
global.decodeURIComponent = globalDecodeURIComponentBefore
});
test('should return next when activity type is not message', () => {
const result = createExtractAdaptiveCardMiddleware()({getState: () => {}})(() => next)({});
expect(result).toBe(next);
});
test('should return next on wrong activity text', () => {
const activity = { text: 'testText', type: ActivityType.Message }
const result = createExtractAdaptiveCardMiddleware()({getState: () => {}})(() => next)(activity);
expect(result).toBe(next);
});
test('should return next on TAG_PARSE_ERROR', () => {
const mockDomParser = {
getElementsByTagName: () => [{}]
}
spyOn(DOMParser.prototype, 'parseFromString').and.returnValue(mockDomParser);
const activity = { text: 'URIObject', type: ActivityType.Message }
const getState = () => ({logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent})
const result = createExtractAdaptiveCardMiddleware()({ getState })(() => next)(activity);
expect(result).toBe(next);
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('WARN', {
Event: TelemetryEvents.ADAPTIVE_CARD_PROCESSING_ERROR,
Description: `Adapter: [AdaptiveCard] Unable to parse XML; ignoring attachment.`
});
});
test('should return next when element nodeName is not CONTENT_URI_OBJECT', () => {
const mockDomParser = {
getElementsByTagName: () => [],
documentElement: { nodeName: 'testNodeName' }
}
spyOn(DOMParser.prototype, 'parseFromString').and.returnValue(mockDomParser);
const activity = { text: 'URIObject', type: ActivityType.Message }
const getState = () => ({logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent})
const result = createExtractAdaptiveCardMiddleware()({ getState })(() => next)(activity);
expect(result).toBe(next);
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('WARN', {
Event: TelemetryEvents.ADAPTIVE_CARD_PROCESSING_ERROR,
Description: `Adapter: [AdaptiveCard] Wrong XML schema; ignoring attachment.`
});
});
test('should return next when no Swift element', () => {
const mockDomParser = {
getElementsByTagName: () => [],
documentElement: { nodeName: 'URIObject' }
}
spyOn(DOMParser.prototype, 'parseFromString').and.returnValue(mockDomParser);
const activity = { text: 'URIObject', type: ActivityType.Message }
const getState = () => ({logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent})
const result = createExtractAdaptiveCardMiddleware()({ getState })(() => next)(activity);
expect(result).toBe(next);
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('WARN', {
Event: TelemetryEvents.ADAPTIVE_CARD_PROCESSING_ERROR,
Description: `Adapter: [AdaptiveCard] Does not contain <Swift>; ignoring attachment.`
});
});
test('should return next when no swiftJSON', () => {
const mockdecodeURIComponent = jest.fn().mockReturnValue('');
global.decodeURIComponent = mockdecodeURIComponent;
const swiftElement = { getAttribute: () => '' }
const mockDomParser = {
getElementsByTagName: (tag) => tag === 'Swift' ? [swiftElement] : [],
documentElement: { nodeName: 'URIObject' }
}
spyOn(DOMParser.prototype, 'parseFromString').and.returnValue(mockDomParser);
const activity = { text: 'URIObject', type: ActivityType.Message }
const getState = () => ({logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent})
const result = createExtractAdaptiveCardMiddleware()({ getState })(() => next)(activity);
expect(result).toBe(next);
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('WARN', {
Event: TelemetryEvents.ADAPTIVE_CARD_PROCESSING_ERROR,
Description: `Adapter: [AdaptiveCard] Data is empty; ignoring attachment.`
});
});
test('should return next when swift type does not include TYPE_MESSAGE_CARD', () => {
const mockSwift = { type: 'message' }
const mockdecodeURIComponent = jest.fn().mockReturnValue(JSON.stringify(mockSwift));
global.decodeURIComponent = mockdecodeURIComponent;
const swiftElement = { getAttribute: () => '' }
const mockDomParser = {
getElementsByTagName: (tag) => tag === 'Swift' ? [swiftElement] : [],
documentElement: { nodeName: 'URIObject' }
}
spyOn(DOMParser.prototype, 'parseFromString').and.returnValue(mockDomParser);
const activity = { text: 'URIObject', type: ActivityType.Message }
const getState = () => ({logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent})
const result = createExtractAdaptiveCardMiddleware()({ getState })(() => next)(activity);
expect(result).toBe(next);
});
test('should return next when no swift attachments', () => {
const mockSwift = { type: 'message/card' }
const mockdecodeURIComponent = jest.fn().mockReturnValue(JSON.stringify(mockSwift));
global.decodeURIComponent = mockdecodeURIComponent;
const swiftElement = { getAttribute: () => '' }
const mockDomParser = {
getElementsByTagName: (tag) => tag === 'Swift' ? [swiftElement] : [],
documentElement: { nodeName: 'URIObject' }
}
spyOn(DOMParser.prototype, 'parseFromString').and.returnValue(mockDomParser);
const activity = { text: 'URIObject', type: ActivityType.Message }
const getState = () => ({logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent})
const result = createExtractAdaptiveCardMiddleware()({ getState })(() => next)(activity);
expect(result).toBe(next);
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('WARN', {
Event: TelemetryEvents.ADAPTIVE_CARD_PROCESSING_ERROR,
Description: `Adapter: [AdaptiveCard] Key 'attachments' not found; ignoring attachment.`
});
});
test('should call next whith correct params', () => {
const mockSwift = { type: 'message/card', attachments: [] }
const mockdecodeURIComponent = jest.fn().mockReturnValue(JSON.stringify(mockSwift));
global.decodeURIComponent = mockdecodeURIComponent;
const swiftElement = { getAttribute: () => '' }
const mockDomParser = {
getElementsByTagName: (tag) => tag === 'Swift' ? [swiftElement] : [],
documentElement: { nodeName: 'URIObject' }
}
spyOn(DOMParser.prototype, 'parseFromString').and.returnValue(mockDomParser);
const activity = { text: 'URIObject', type: ActivityType.Message }
const getState = () => ({logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent})
const result = createExtractAdaptiveCardMiddleware()({ getState })((param) => param)(activity);
const expectedResult = { attachments: mockSwift.attachments, text: '', type: 'message' };
expect(result).toEqual(expectedResult);
});
});

Просмотреть файл

@ -0,0 +1,60 @@
import createPatchFromRoleAndNameMiddleware from './../../../../src/ic3/enhancers/ingress/createPatchFromRoleAndNameMiddleware';
import { StateKey } from './../../../../src/types/ic3/IC3AdapterState';
import { Role } from './../../../../src/types/DirectLineTypes';
describe('createPatchFromRoleAndNameMiddleware test', () => {
const next = jest.fn().mockReturnValue('next');
test('should call next with correct params', () => {
const getState = (key) => key === StateKey.UserId ? 'userId' : key === StateKey.UserDisplayName ? 'userName' : null;
const activity = {
from: {
id: 'activityId',
name: 'activityName',
role: Role.User
}
}
const result = createPatchFromRoleAndNameMiddleware()({ getState })(next)(activity);
expect(result).toBe('next');
expect(next).toHaveBeenCalledWith({
from: {
id: 'activityId',
name: 'activityName',
role: Role.Bot
}
});
});
test('should call next with correct params', () => {
const getState = (key) => key === StateKey.UserId ? 'userId' : key === StateKey.UserDisplayName ? 'userName' : null;
const activity = {
from: {
id: 'userIdActivity',
name: 'activityName',
role: Role.User
}
}
const result = createPatchFromRoleAndNameMiddleware()({ getState })(next)(activity);
expect(result).toBe('next');
expect(next).toHaveBeenCalledWith({
from: {
id: 'userIdActivity',
name: 'userName',
role: Role.User
}
});
});
test('should call next with correct params', () => {
const getState = (key) => key === StateKey.UserId ? 'userId' : key === StateKey.UserDisplayName ? 'userName' : null;
const activity = {
from: {
id: 'userIdActivity',
name: 'activityName',
role: Role.Channel
}
}
const result = createPatchFromRoleAndNameMiddleware()({ getState })(next)(activity);
expect(result).toBe('next');
expect(next).toHaveBeenCalledWith({...activity});
});
});

Просмотреть файл

@ -0,0 +1,20 @@
import createIngressEnhancer from './../../../../src/ic3/enhancers/ingress/index';
import * as redux from 'redux';
import * as createExtractAdaptiveCardMiddleware from './../../../../src/ic3/enhancers/ingress/createExtractAdaptiveCardMiddleware';
import * as createPatchFromRoleAndNameMiddleware from './../../../../src/ic3/enhancers/ingress/createPatchFromRoleAndNameMiddleware';
import * as createSubscribeNewMessageAndThreadUpdateEnhancer from './../../../../src/ic3/enhancers/ingress/subscribeNewMessageAndThreadUpdate';
import * as applyIngressMiddleware from './../../../../src/applyIngressMiddleware';
describe('createIngressEnhancer test', () => {
test('should call compose with correct params', () => {
spyOn(redux, 'compose').and.returnValue('compose');
applyIngressMiddleware.default = jest.fn().mockReturnValue('mock1');
createExtractAdaptiveCardMiddleware.default = jest.fn().mockReturnValue('mock2');
createPatchFromRoleAndNameMiddleware.default = jest.fn().mockReturnValue('mock3');
createSubscribeNewMessageAndThreadUpdateEnhancer.default = jest.fn().mockReturnValue('mock4');
const result = createIngressEnhancer();
expect(redux.compose).toHaveBeenCalledWith('mock4', 'mock1');
expect(applyIngressMiddleware.default).toHaveBeenCalledWith('mock3', 'mock2');
expect(result).toBe('compose')
})
});

Просмотреть файл

@ -0,0 +1,78 @@
import createTypingMessageToDirectLineActivityMapper from './../../../../../src/ic3/enhancers/ingress/mappers/createThreadToDirectLineActivityMapper';
import { StateKey } from './../../../../../src/types/ic3/IC3AdapterState';
import { TelemetryEvents } from './../../../../../src/types/ic3/TelemetryEvents';
import { IC3_CHANNEL_ID } from './../../../../../src/ic3/Constants';
import { ActivityType, Role } from './../../../../../src/types/DirectLineTypes';
import * as uniqueId from './../../../../../src/ic3/utils/uniqueId';
describe('createTypingMessageToDirectLineActivityMapper test', () => {
let globalMicrosoftBefore;
beforeAll(() => {
globalMicrosoftBefore = global.Microsoft;
global.Microsoft = {
CRM: {
Omnichannel: {
IC3Client: {
Model: {
LogLevel: { DEBUG: 'DEBUG', ERROR: 'ERROR' }
}
}
},
}
}
});
afterAll(() => {
global.Microsoft = globalMicrosoftBefore;
});
test('should throw error when no conversation', async () => {
const mockLogClientSdkTelemetryEvent = jest.fn();
const mockGetState = (key) => key === StateKey.Logger
? { logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent }
: null;
try {
await createTypingMessageToDirectLineActivityMapper({getState: mockGetState})()();
} catch(e) {
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('ERROR', {
Event: TelemetryEvents.CONVERSATION_NOT_FOUND,
Description: `Adapter: Failed to ingress thread update without an active conversation.`
});
expect(e).toEqual(new Error('IC3: Failed to ingress thread update without an active conversation.'))
}
});
test('should return correct ic3 activity', async () => {
spyOn(Date.prototype, 'toISOString').and.returnValue('dateString');
spyOn(uniqueId, 'default').and.returnValue('uniqueId');
const conversation = { id: 'convId' };
const mockGetState = () => conversation;
const thread = {
id: 'threadId',
members: 'members',
properties: 'properties',
type: 'type'
};
const result = await createTypingMessageToDirectLineActivityMapper({getState: mockGetState})()(thread);
const expectedResult = {
channelData: {
members: thread.members,
properties: thread.properties,
type: thread.type
},
channelId: IC3_CHANNEL_ID,
conversation,
from: {
id: thread.id,
name: '',
role: Role.Channel
},
id: 'uniqueId',
timestamp: 'dateString',
type: ActivityType.Message
}
expect(result).toEqual(expectedResult);
});
});

Просмотреть файл

@ -0,0 +1,73 @@
import createTypingMessageToDirectLineActivityMapper from './../../../../../src/ic3/enhancers/ingress/mappers/createTypingMessageToDirectLineActivityMapper';
import { StateKey } from './../../../../../src/types/ic3/IC3AdapterState';
import { TelemetryEvents } from './../../../../../src/types/ic3/TelemetryEvents';
import { IC3_CHANNEL_ID } from './../../../../../src/ic3/Constants';
import { ActivityType } from './../../../../../src/types/DirectLineTypes';
import * as uniqueId from './../../../../../src/ic3/utils/uniqueId';
describe('createTypingMessageToDirectLineActivityMapper test', () => {
let globalMicrosoftBefore;
beforeAll(() => {
globalMicrosoftBefore = global.Microsoft;
global.Microsoft = {
CRM: {
Omnichannel: {
IC3Client: {
Model: {
LogLevel: { ERROR: 'ERROR' },
MessageType: { Typing: 'Typing', ClearTyping: 'ClearTyping'}
}
}
},
}
}
});
afterAll(() => {
global.Microsoft = globalMicrosoftBefore;
});
test('should return next', async () => {
const next = () => 'next';
const result = await createTypingMessageToDirectLineActivityMapper({})(next)({});
expect(result).toBe(next());
});
test('should throw error when no conversation', async () => {
const mockLogClientSdkTelemetryEvent = jest.fn();
const getState = (key) => key === StateKey.Logger ? {logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent} : null;
const message = { messageType: 'Typing' };
try {
await createTypingMessageToDirectLineActivityMapper({ getState })()(message);
} catch(e) {
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('ERROR', {
Event: TelemetryEvents.CONVERSATION_NOT_FOUND,
Description: `Adapter: Failed to ingress typing without an active conversation.`
});
expect(e).toEqual(new Error('IC3: Failed to ingress typing without an active conversation.'));
}
});
test('should return correct object', async () => {
spyOn(Date.prototype, 'toISOString').and.returnValue('dateString');
spyOn(uniqueId, 'default').and.returnValue('uniqueId');
const conversation = { id: 'tesConvId' };
const getState = () => conversation;
const message = {
messageType: 'Typing',
timestamp: new Date(),
sender: { displayName: 'displayName', id: 'senderId' }
};
const result = await createTypingMessageToDirectLineActivityMapper({ getState })()(message);
const expectedResult = {
channelId: IC3_CHANNEL_ID,
conversation,
from: { id: message.sender.id, name: message.sender.displayName },
id: 'uniqueId',
timestamp: 'dateString',
type: ActivityType.Typing
}
expect(result).toEqual(expectedResult);
});
});

Просмотреть файл

@ -0,0 +1,286 @@
import createUserMessageToDirectLineActivityMapper from './../../../../../src/ic3/enhancers/ingress/mappers/createUserMessageToDirectLineActivityMapper';
import { StateKey } from './../../../../../src/types/ic3/IC3AdapterState';
import { TelemetryEvents } from './../../../../../src/types/ic3/TelemetryEvents';
import { IC3_CHANNEL_ID } from './../../../../../src/ic3/Constants';
import { ActivityType } from './../../../../../src/types/DirectLineTypes';
describe('createUserMessageToDirectLineActivityMapper test', () => {
let globalMicrosoftBefore;
let globalURLBefore;
beforeAll(() => {
spyOn(Date.prototype, 'toISOString').and.returnValue('dateString');
globalMicrosoftBefore = global.Microsoft;
globalURLBefore = global.URL;
global.URL.createObjectURL = jest.fn().mockReturnValue('objectUrl');
global.Microsoft = {
CRM: {
Omnichannel: {
IC3Client: {
Model: {
LogLevel: { ERROR: 'ERROR' },
MessageType: { UserMessage: 'UserMessage' }
}
}
},
}
}
});
afterAll(() => {
global.Microsoft = globalMicrosoftBefore;
global.URL = globalURLBefore;
});
test('should return next', async () => {
const next = 'next';
const result = await createUserMessageToDirectLineActivityMapper({getState: () => {}})(() => next)({});
expect(result).toBe(next);
});
test('should throw error', async () => {
const mockLogClientSdkTelemetryEvent = jest.fn();
const message = { messageType : 'UserMessage' };
const getState = (key) => key === StateKey.Logger ? { logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent } : null;
try {
await createUserMessageToDirectLineActivityMapper({getState})()(message);
} catch(e) {
expect(e).toEqual(new Error('IC3: Failed to ingress message without an active conversation.'));
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('ERROR', {
Event: TelemetryEvents.CONVERSATION_NOT_FOUND,
Description: `Adapter: Failed to ingress message without an active conversation.`
});
}
});
test('should return correct activity', async () => {
const message = {
messageType : 'UserMessage',
messageid: 'messageid',
clientmessageid: 'clientmessageid',
content: 'content',
properties: { fromUserId: 'fromUserId' },
sender: { displayName: 'displayName', id: 'senderId' },
timestamp: new Date()
};
const conversation = { id: 'conversationId' }
const getState = () => conversation;
const result = await createUserMessageToDirectLineActivityMapper({getState})()(message);
const expectedResult = {
attachments: undefined,
channelData: {
clientmessageid: message.clientmessageid,
fromUserId: 'fromUserId',
tags: undefined
},
channelId: IC3_CHANNEL_ID,
conversation,
from: { id: message.sender.id, name: message.sender.displayName },
id: message.clientmessageid,
messageid: message.messageid,
suggestedActions: undefined,
text: message.content,
timestamp: 'dateString',
type: ActivityType.Message
}
expect(result).toEqual(expectedResult);
});
test('should return correct activity with attachments', async () => {
const message = {
messageType : 'UserMessage',
messageid: 'messageid',
clientmessageid: 'clientmessageid',
content: 'content',
properties: { fromUserId: 'fromUserId' },
sender: { displayName: 'displayName', id: 'senderId' },
timestamp: new Date(),
fileMetadata: { type: 'fileType' },
tags: ['tetTag']
};
const conversation = {
id: 'conversationId',
downloadFile: () => 'downloadFile'
}
const getState = () => conversation;
const result = await createUserMessageToDirectLineActivityMapper({getState})()(message);
const expectedResult = {
attachments: [{
blob: 'downloadFile',
contentType: 'application/octet-stream',
contentUrl: 'objectUrl',
tempContentUrl: 'objectUrl',
thumbnailUrl: undefined,
type: 'fileType'
}],
channelData: {
clientmessageid: message.clientmessageid,
fromUserId: message.properties.fromUserId,
tags: message.tags
},
channelId: IC3_CHANNEL_ID,
conversation: { id: conversation.id },
from: { id: message.sender.id, name: message.sender.displayName },
id: message.clientmessageid,
messageid: message.messageid,
suggestedActions: undefined,
text: message.content,
timestamp: 'dateString',
type: ActivityType.Message,
}
expect(result).toEqual(expectedResult);
});
test('should return correct activity with image attachment', async () => {
const message = {
messageType : 'UserMessage',
messageid: 'messageid',
clientmessageid: 'clientmessageid',
content: 'content',
properties: { fromUserId: 'fromUserId' },
sender: { displayName: 'displayName', id: 'senderId' },
timestamp: new Date(),
fileMetadata: { type: 'gif' },
tags: ['client_activity_id:']
};
const conversation = {
id: 'conversationId',
downloadFile: () => 'downloadFile'
}
const getState = () => conversation;
const result = await createUserMessageToDirectLineActivityMapper({getState})()(message);
const expectedResult = {
attachments: [{
blob: 'downloadFile',
contentType: 'image/gif',
contentUrl: 'objectUrl',
tempContentUrl: 'objectUrl',
thumbnailUrl: 'objectUrl',
type: 'gif'
}],
channelData: {
clientActivityID: '',
clientmessageid: message.clientmessageid,
fromUserId: message.properties.fromUserId,
tags: message.tags
},
channelId: IC3_CHANNEL_ID,
conversation: { id: conversation.id },
from: { id: message.sender.id, name: message.sender.displayName },
id: message.clientmessageid,
messageid: message.messageid,
suggestedActions: undefined,
text: message.content,
timestamp: 'dateString',
type: ActivityType.Message,
}
expect(result).toEqual(expectedResult);
});
test('should return correct activity with audio attachment', async () => {
const message = {
messageType : 'UserMessage',
messageid: 'messageid',
clientmessageid: 'clientmessageid',
content: 'content',
properties: { fromUserId: 'fromUserId' },
sender: { displayName: 'displayName', id: 'senderId' },
timestamp: new Date(),
fileMetadata: { type: 'mp3' },
tags: ['client_activity_id:']
};
const conversation = {
id: 'conversationId',
downloadFile: () => 'downloadFile'
}
const getState = (key) => {
switch(key) {
case StateKey.FeatureConfig: return {
ShouldEnableInlineAudioPlaying: true,
}
case StateKey.Conversation: return conversation;
default: return null
}
};
const result = await createUserMessageToDirectLineActivityMapper({getState})()(message);
const expectedResult = {
attachments: [{
blob: 'downloadFile',
contentType: 'audio/mp3',
contentUrl: 'objectUrl',
tempContentUrl: 'objectUrl',
thumbnailUrl: 'objectUrl',
type: 'mp3'
}],
channelData: {
clientActivityID: '',
clientmessageid: message.clientmessageid,
fromUserId: message.properties.fromUserId,
tags: message.tags
},
channelId: IC3_CHANNEL_ID,
conversation: { id: conversation.id },
from: { id: message.sender.id, name: message.sender.displayName },
id: message.clientmessageid,
messageid: message.messageid,
suggestedActions: undefined,
text: message.content,
timestamp: 'dateString',
type: ActivityType.Message,
}
expect(result).toEqual(expectedResult);
});
test('should return correct activity with video attachment', async () => {
const message = {
messageType : 'UserMessage',
messageid: 'messageid',
clientmessageid: 'clientmessageid',
content: 'content',
properties: { fromUserId: 'fromUserId' },
sender: { displayName: 'displayName', id: 'senderId' },
timestamp: new Date(),
fileMetadata: { type: 'mp4' },
tags: ['client_activity_id:']
};
const conversation = {
id: 'conversationId',
downloadFile: () => 'downloadFile'
}
const getState = (key) => {
switch(key) {
case StateKey.FeatureConfig: return {
ShouldEnableInlineVideoPlaying: true
}
case StateKey.Conversation: return conversation;
default: return null
}
};
const result = await createUserMessageToDirectLineActivityMapper({getState})()(message);
const expectedResult = {
attachments: [{
blob: 'downloadFile',
contentType: 'video/mp4',
contentUrl: 'objectUrl',
tempContentUrl: 'objectUrl',
thumbnailUrl: 'objectUrl',
type: 'mp4'
}],
channelData: {
clientActivityID: '',
clientmessageid: message.clientmessageid,
fromUserId: message.properties.fromUserId,
tags: message.tags
},
channelId: IC3_CHANNEL_ID,
conversation: { id: conversation.id },
from: { id: message.sender.id, name: message.sender.displayName },
id: message.clientmessageid,
messageid: message.messageid,
suggestedActions: undefined,
text: message.content,
timestamp: 'dateString',
type: ActivityType.Message,
}
expect(result).toEqual(expectedResult);
});
});

Просмотреть файл

@ -0,0 +1,115 @@
import createSubscribeNewMessageAndThreadUpdateEnhancer from './../../../../src/ic3/enhancers/ingress/subscribeNewMessageAndThreadUpdate';
import * as redux from 'redux';
import * as applySetStateMiddleware from './../../../../src/applySetStateMiddleware';
import * as createThreadToDirectLineActivityMapper from './../../../../src/ic3/enhancers/ingress/mappers/createThreadToDirectLineActivityMapper';
import { StateKey } from './../../../../src/types/ic3/IC3AdapterState';
import { TelemetryEvents } from './../../../../src/types/ic3/TelemetryEvents';
import { ReadyState } from './../../../../src/types/AdapterTypes';
import ConnectivityManager from './../../../../src/ic3/utils/ConnectivityManager';
describe('createSubscribeNewMessageAndThreadUpdateEnhancer test', () => {
let globalMicrosoftBefore;
let windowAddEventListenerBefore;
const mockLogClientSdkTelemetryEvent = jest.fn();
const subscribe = subscriber => { subscriber.subscribe()}
applySetStateMiddleware.default = (param) => param;
const next = jest.fn();
beforeAll(() => {
jest.useFakeTimers();
windowAddEventListenerBefore = window.addEventListener;
globalMicrosoftBefore = global.Microsoft;
window.addEventListener = jest.fn((param1, param2) => param2());
redux.compose = () => param => { param() }
createThreadToDirectLineActivityMapper.default = () => param => { param() }
spyOn(redux, 'compose').and.callThrough();
spyOn(createThreadToDirectLineActivityMapper, 'default').and.callThrough();
global.Microsoft = {
CRM: {
Omnichannel: {
IC3Client: {
Model: {
LogLevel: {
WARN: 'WARN',
DEBUG: 'DEBUG',
INFO: 'INFO',
ERROR: 'ERROR'
},
IConversation: {}
}
}
},
}
}
});
afterAll(() => {
global.Microsoft = globalMicrosoftBefore;
window.addEventListener = windowAddEventListenerBefore;
});
test('should make correct calls when isInternetConnected is true, getReadyState is open', async () => {
spyOn(ConnectivityManager, 'isInternetConnected').and.returnValue(true);
const conversation = {
getMessages: () => ['message1', 'mmesage2'],
registerOnNewMessage: (param) => { param() },
registerOnThreadUpdate: (param) => { param() }
}
const getState = (key) => {
switch(key) {
case StateKey.Logger: return {
logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent
}
case StateKey.ConnectionStatusObserverReady: return 'ConnectionStatusObserverReady';
default: return null;
}
}
await createSubscribeNewMessageAndThreadUpdateEnhancer()({
getState,
subscribe,
getReadyState: () => ReadyState.OPEN
})(next)(StateKey.Conversation, conversation);
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('DEBUG', {
Event: TelemetryEvents.REHYDRATE_MESSAGES,
Description: `Adapter: Re-hydrating received messages`
});
expect(window.addEventListener).toHaveBeenCalledWith('reinitialize', expect.any(Function));
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('DEBUG', {
Event: TelemetryEvents.REGISTER_ON_NEW_MESSAGE,
Description: `Adapter: Registering on new message success`
});
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('DEBUG', {
Event: TelemetryEvents.GET_MESSAGES_SUCCESS,
Description: `Adapter: Getting messages success`
});
expect(redux.compose).toHaveBeenCalled();
});
test('should make correct calls when isInternetConnected is false, getReadyState is not open', async () => {
spyOn(ConnectivityManager, 'isInternetConnected').and.returnValue(false);
const conversation = {
getMessages: () => ['message1', 'mmesage2'],
registerOnNewMessage: (param) => { param() },
registerOnThreadUpdate: (param) => { param() }
}
const getState = (key) => {
switch(key) {
case StateKey.Logger: return {
logClientSdkTelemetryEvent: mockLogClientSdkTelemetryEvent
}
default: return null;
}
}
await createSubscribeNewMessageAndThreadUpdateEnhancer()({
getState,
subscribe,
getReadyState: () => {}
})(next)(StateKey.Conversation, conversation);
jest.runAllTimers();
expect(mockLogClientSdkTelemetryEvent).toHaveBeenCalledWith('DEBUG', {
Event: TelemetryEvents.REHYDRATE_MESSAGES,
Description: `Adapter: Re-hydrating received messages`
});
expect(window.addEventListener).toHaveBeenCalledWith('reinitialize', expect.any(Function));
expect(redux.compose).toHaveBeenCalled();
});
});