test: Improve tests for legacy omnichannel analytics (#30379)

This commit is contained in:
Murtaza Patrawala 2023-10-06 22:35:17 +04:00 committed by GitHub
parent 75f0ae31d9
commit df5c496487
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 523 additions and 42 deletions

View File

@ -2,9 +2,9 @@ import { faker } from '@faker-js/faker';
import { expect } from 'chai';
import type { ILivechatDepartment, IUser, LivechatDepartmentDTO } from '@rocket.chat/core-typings';
import { api, credentials, methodCall, request } from '../api-data';
import { IUserCredentialsHeader, password } from '../user';
import { login } from '../users.helper';
import { createAgent, makeAgentAvailable } from './rooms';
import { IUserCredentialsHeader } from '../user';
import { createAnOnlineAgent } from './users';
import { WithRequiredProperty } from './utils';
export const NewDepartmentData = ((): Partial<ILivechatDepartment> => ({
enabled: true,
@ -59,29 +59,19 @@ new Promise((resolve, reject) => {
export const createDepartmentWithAnOnlineAgent = async (): Promise<{department: ILivechatDepartment, agent: {
credentials: IUserCredentialsHeader;
user: IUser;
user: WithRequiredProperty<IUser, 'username'>;
}}> => {
// TODO moving here for tests
const username = `user.test.${Date.now()}`;
const email = `${username}@rocket.chat`;
const { body } = await request
.post(api('users.create'))
.set(credentials)
.send({ email, name: username, username, password });
const agent = body.user;
const createdUserCredentials = await login(agent.username, password);
await createAgent(agent.username);
await makeAgentAvailable(createdUserCredentials);
const { user, credentials } = await createAnOnlineAgent();
const department = await createDepartmentWithMethod() as ILivechatDepartment;
await addOrRemoveAgentFromDepartment(department._id, {agentId: agent._id, username: (agent.username as string)}, true);
await addOrRemoveAgentFromDepartment(department._id, {agentId: user._id, username: user.username}, true);
return {
department,
agent: {
credentials: createdUserCredentials,
user: agent,
credentials,
user,
}
};
};

View File

@ -185,6 +185,9 @@ export const getLivechatRoomInfo = (roomId: string): Promise<IOmnichannelRoom> =
});
};
/**
* @summary Sends message as visitor
*/
export const sendMessage = (roomId: string, message: string, visitorToken: string): Promise<IMessage> => {
return new Promise((resolve, reject) => {
request

View File

@ -1,6 +1,6 @@
import { faker } from "@faker-js/faker";
import type { IUser } from "@rocket.chat/core-typings";
import { password } from "../user";
import { IUserCredentialsHeader, password } from "../user";
import { createUser, login } from "../users.helper";
import { createAgent, makeAgentAvailable } from "./rooms";
import { api, credentials, request } from "../api-data";
@ -29,3 +29,24 @@ export const removeAgent = async (userId: string): Promise<void> => {
.set(credentials)
.expect(200);
}
export const createAnOnlineAgent = async (): Promise<{
credentials: IUserCredentialsHeader;
user: IUser & { username: string };
}> => {
const username = `user.test.${Date.now()}`;
const email = `${username}@rocket.chat`;
const { body } = await request
.post(api('users.create'))
.set(credentials)
.send({ email, name: username, username, password });
const agent = body.user;
const createdUserCredentials = await login(agent.username, password);
await createAgent(agent.username);
await makeAgentAvailable(createdUserCredentials);
return {
credentials: createdUserCredentials,
user: agent,
};
}

View File

@ -1,6 +1,10 @@
export type DummyResponse<T, E = 'wrapped'> =
E extends 'wrapped' ? { body: { [k: string]: T } } : { body: T };
export type WithRequiredProperty<Type, Key extends keyof Type> = Type & {
[Property in Key]-?: Type[Property];
};
export const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
}

View File

@ -1,12 +1,30 @@
import { faker } from '@faker-js/faker';
import type { ILivechatDepartment, IUser } from '@rocket.chat/core-typings';
import { Random } from '@rocket.chat/random';
import { expect } from 'chai';
import { before, describe, it } from 'mocha';
import moment from 'moment';
import type { Response } from 'supertest';
import { getCredentials, api, request, credentials } from '../../../data/api-data';
import { updatePermission, updateSetting } from '../../../data/permissions.helper';
import { addOrRemoveAgentFromDepartment, createDepartmentWithAnOnlineAgent } from '../../../data/livechat/department';
import {
closeOmnichannelRoom,
placeRoomOnHold,
sendAgentMessage,
sendMessage,
startANewLivechatRoomAndTakeIt,
} from '../../../data/livechat/rooms';
import { createAnOnlineAgent } from '../../../data/livechat/users';
import { sleep } from '../../../data/livechat/utils';
import { removePermissionFromAllRoles, restorePermissionToRoles, updateSetting } from '../../../data/permissions.helper';
import type { IUserCredentialsHeader } from '../../../data/user';
import { IS_EE } from '../../../e2e/config/constants';
describe('LIVECHAT - dashboards', function () {
this.retries(0);
// This test is expected to take more time since we're simulating real time conversations to verify analytics
this.timeout(60000);
before((done) => getCredentials(done));
@ -14,6 +32,106 @@ describe('LIVECHAT - dashboards', function () {
await updateSetting('Livechat_enabled', true);
});
let department: ILivechatDepartment;
const agents: {
credentials: IUserCredentialsHeader;
user: IUser & { username: string };
}[] = [];
let avgClosedRoomChatDuration = 0;
const inactivityTimeout = 3;
const TOTAL_MESSAGES = {
min: 5,
max: 10,
};
const DELAY_BETWEEN_MESSAGES = {
min: 1000,
max: (inactivityTimeout - 1) * 1000,
};
const TOTAL_ROOMS = 7;
const simulateRealtimeConversation = async (chatInfo: Awaited<ReturnType<typeof startANewLivechatRoomAndTakeIt>>[]) => {
const promises = chatInfo.map(async (info) => {
const { room, visitor } = info;
// send a few messages
const numberOfMessages = Random.between(TOTAL_MESSAGES.min, TOTAL_MESSAGES.max);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for await (const _ of Array(numberOfMessages - 1).keys()) {
// flip a coin to decide who will send the message
const willSendFromAgent = Random.between(0, 1) === 1;
if (willSendFromAgent) {
await sendAgentMessage(room._id);
} else {
await sendMessage(room._id, faker.lorem.sentence(), visitor.token);
}
const delay = Random.between(DELAY_BETWEEN_MESSAGES.min, DELAY_BETWEEN_MESSAGES.max);
await sleep(delay);
}
// Last message is always from visitor so that the chat doesn't get abandoned due to
// "Livechat_visitor_inactivity_timeout" setting
await sendMessage(room._id, faker.lorem.sentence(), visitor.token);
});
await Promise.all(promises);
};
before(async () => {
if (!IS_EE) {
return;
}
await updateSetting('Livechat_visitor_inactivity_timeout', inactivityTimeout);
await updateSetting('Livechat_enable_business_hours', false);
// create dummy test data for further tests
const { department: createdDept, agent: agent1 } = await createDepartmentWithAnOnlineAgent();
department = createdDept;
console.log('department', department.name);
const agent2 = await createAnOnlineAgent();
await addOrRemoveAgentFromDepartment(department._id, { agentId: agent2.user._id, username: agent2.user.username }, true);
agents.push(agent1);
agents.push(agent2);
const roomCreationStart = moment();
// start a few chats
const promises = Array.from(Array(TOTAL_ROOMS).keys()).map((i) => {
// 2 rooms by agent 1
if (i < 2) {
return startANewLivechatRoomAndTakeIt({ departmentId: department._id, agent: agent1.credentials });
}
return startANewLivechatRoomAndTakeIt({ departmentId: department._id, agent: agent2.credentials });
});
const results = await Promise.all(promises);
const chatInfo = results.map((result) => ({ room: result.room, visitor: result.visitor }));
// simulate messages being exchanged between agents and visitors
await simulateRealtimeConversation(chatInfo);
// put a chat on hold
await sendAgentMessage(chatInfo[1].room._id);
await placeRoomOnHold(chatInfo[1].room._id);
// close a chat
await closeOmnichannelRoom(chatInfo[4].room._id);
const room5ChatDuration = moment().diff(roomCreationStart, 'seconds');
// close an abandoned chat
await sendAgentMessage(chatInfo[5].room._id);
await sleep(inactivityTimeout * 1000); // wait for the chat to be considered abandoned
await closeOmnichannelRoom(chatInfo[5].room._id);
const room6ChatDuration = moment().diff(roomCreationStart, 'seconds');
avgClosedRoomChatDuration = (room5ChatDuration + room6ChatDuration) / 2;
});
describe('livechat/analytics/dashboards/conversation-totalizers', () => {
const expectedMetrics = [
'Total_conversations',
@ -25,7 +143,7 @@ describe('LIVECHAT - dashboards', function () {
'Total_visitors',
];
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/conversation-totalizers?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -33,7 +151,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an array of conversation totalizers', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/conversation-totalizers?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -47,12 +165,51 @@ describe('LIVECHAT - dashboards', function () {
);
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/conversation-totalizers'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(result.body).to.have.property('success', true);
expect(result.body).to.have.property('totalizers');
expect(result.body.totalizers).to.be.an('array');
expect(result.body.totalizers).to.have.lengthOf(5);
const expectedResult = [
{ title: 'Total_conversations', value: 7 },
{ title: 'Open_conversations', value: 4 },
{ title: 'On_Hold_conversations', value: 1 },
// { title: 'Total_messages', value: 60 },
{ title: 'Total_visitors', value: 7 },
];
expectedResult.forEach((expected) => {
const resultItem = result.body.totalizers.find((item: any) => item.title === expected.title);
expect(resultItem).to.not.be.undefined;
expect(resultItem).to.have.property('value', expected.value);
});
const minMessages = TOTAL_MESSAGES.min * TOTAL_ROOMS;
const maxMessages = TOTAL_MESSAGES.max * TOTAL_ROOMS;
const totalMessages = result.body.totalizers.find((item: any) => item.title === 'Total_messages');
expect(totalMessages).to.not.be.undefined;
const totalMessagesValue = parseInt(totalMessages.value);
expect(totalMessagesValue).to.be.greaterThanOrEqual(minMessages);
expect(totalMessagesValue).to.be.lessThanOrEqual(maxMessages);
});
});
describe('livechat/analytics/dashboards/productivity-totalizers', () => {
const expectedMetrics = ['Avg_response_time', 'Avg_first_response_time', 'Avg_reaction_time', 'Avg_of_waiting_time'];
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/productivity-totalizers?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -60,7 +217,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an array of productivity totalizers', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/productivity-totalizers?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -74,12 +231,41 @@ describe('LIVECHAT - dashboards', function () {
);
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/productivity-totalizers'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(result.body).to.have.property('success', true);
// const expected = [
// // There's a bug in the code for calculation of these 3 values.
// // Due to which it always return 0
// { title: 'Avg_response_time', value: '00:00:00' },
// { title: 'Avg_first_response_time', value: '00:00:00' },
// { title: 'Avg_reaction_time', value: '00:00:00' },
// { title: 'Avg_of_waiting_time', value: '00:00:03' }, // approx 3, 5 delta
// ];
const avgWaitingTime = result.body.totalizers.find((item: any) => item.title === 'Avg_of_waiting_time');
expect(avgWaitingTime).to.not.be.undefined;
const avgWaitingTimeValue = moment.duration(avgWaitingTime.value).asSeconds();
expect(avgWaitingTimeValue).to.be.closeTo(DELAY_BETWEEN_MESSAGES.max / 1000, 5);
});
});
describe('livechat/analytics/dashboards/chats-totalizers', () => {
const expectedMetrics = ['Total_abandoned_chats', 'Avg_of_abandoned_chats', 'Avg_of_chat_duration_time'];
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/chats-totalizers?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -87,7 +273,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an array of chats totalizers', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/chats-totalizers?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -101,12 +287,45 @@ describe('LIVECHAT - dashboards', function () {
);
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/chats-totalizers'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
const expected = [
{ title: 'Total_abandoned_chats', value: 1 },
{ title: 'Avg_of_abandoned_chats', value: '14%' },
// { title: 'Avg_of_chat_duration_time', value: '00:00:01' },
];
expect(result.body).to.have.property('success', true);
expect(result.body).to.have.property('totalizers');
expect(result.body.totalizers).to.be.an('array');
expected.forEach((expected) => {
const resultItem = result.body.totalizers.find((item: any) => item.title === expected.title);
expect(resultItem).to.not.be.undefined;
expect(resultItem).to.have.property('value', expected.value);
});
const resultAverageChatDuration = result.body.totalizers.find((item: any) => item.title === 'Avg_of_chat_duration_time');
expect(resultAverageChatDuration).to.not.be.undefined;
const resultAverageChatDurationValue = moment.duration(resultAverageChatDuration.value).asSeconds();
expect(resultAverageChatDurationValue).to.be.closeTo(avgClosedRoomChatDuration, 5); // Keep a margin of 3 seconds
});
});
describe('livechat/analytics/dashboards/agents-productivity-totalizers', () => {
const expectedMetrics = ['Busiest_time', 'Avg_of_available_service_time', 'Avg_of_service_time'];
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(
api('livechat/analytics/dashboards/agents-productivity-totalizers?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'),
@ -116,7 +335,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an array of agents productivity totalizers', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(
api('livechat/analytics/dashboards/agents-productivity-totalizers?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'),
@ -132,11 +351,40 @@ describe('LIVECHAT - dashboards', function () {
);
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/agents-productivity-totalizers'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
// [
// { title: 'Busiest_time', value: '- -' },
// { title: 'Avg_of_available_service_time', value: '00:00:00' },
// { title: 'Avg_of_service_time', value: '00:00:16' } approx 17, 6 delta
// ],
expect(result.body).to.have.property('success', true);
expect(result.body).to.have.property('totalizers');
expect(result.body.totalizers).to.be.an('array');
const avgServiceTime = result.body.totalizers.find((item: any) => item.title === 'Avg_of_service_time');
expect(avgServiceTime).to.not.be.undefined;
const avgServiceTimeValue = moment.duration(avgServiceTime.value).asSeconds();
const minChatDuration = (DELAY_BETWEEN_MESSAGES.min * TOTAL_MESSAGES.min) / 1000;
const maxChatDuration = (DELAY_BETWEEN_MESSAGES.max * TOTAL_MESSAGES.max) / 1000;
expect(avgServiceTimeValue).to.be.closeTo((minChatDuration + maxChatDuration) / 2, 10);
});
});
describe('livechat/analytics/dashboards/charts/chats', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/chats?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -144,7 +392,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an array of productivity totalizers', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/chats?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -157,11 +405,35 @@ describe('LIVECHAT - dashboards', function () {
expect(res.body).to.have.property('queued');
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/charts/chats'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
const expected = {
open: 4,
closed: 2,
queued: 0,
onhold: 1,
};
expect(result.body).to.have.property('success', true);
Object.entries(expected).forEach(([key, value]) => {
expect(result.body).to.have.property(key, value);
});
});
});
describe('livechat/analytics/dashboards/charts/chats-per-agent', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/chats-per-agent?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -169,7 +441,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an object with open and closed chats by agent', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/chats-per-agent?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -179,11 +451,39 @@ describe('LIVECHAT - dashboards', function () {
expect(res.body).to.have.property('success', true);
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/charts/chats-per-agent'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
const expected = {
agent0: { open: 1, closed: 0, onhold: 1 },
agent1: { open: 3, closed: 2 },
};
expect(result.body).to.have.property('success', true);
const agent0 = result.body[agents[0].user.username as string];
const agent1 = result.body[agents[1].user.username as string];
Object.entries(expected.agent0).forEach(([key, value]) => {
expect(agent0).to.have.property(key, value);
});
Object.entries(expected.agent1).forEach(([key, value]) => {
expect(agent1).to.have.property(key, value);
});
});
});
describe('livechat/analytics/dashboards/charts/agents-status', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/agents-status'))
.set(credentials)
@ -191,7 +491,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an object with agents status metrics', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/agents-status'))
.set(credentials)
@ -205,11 +505,36 @@ describe('LIVECHAT - dashboards', function () {
expect(res.body).to.have.property('available');
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/charts/agents-status'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
// TODO: We can improve tests further by creating some agents with different status
const expected = {
offline: 0,
away: 0,
busy: 0,
available: 2,
};
expect(result.body).to.have.property('success', true);
Object.entries(expected).forEach(([key, value]) => {
expect(result.body).to.have.property(key, value);
});
});
});
describe('livechat/analytics/dashboards/charts/chats-per-department', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/chats-per-department?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -217,7 +542,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an object with open and closed chats by department', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/chats-per-department?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -227,11 +552,34 @@ describe('LIVECHAT - dashboards', function () {
expect(res.body).to.have.property('success', true);
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/charts/chats-per-department'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
const expected = {
department0: { open: 5, closed: 2 },
};
expect(result.body).to.have.property('success', true);
const department0 = result.body[department.name];
Object.entries(expected.department0).forEach(([key, value]) => {
expect(department0).to.have.property(key, value);
});
});
});
describe('livechat/analytics/dashboards/charts/timings', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/timings?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -239,7 +587,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an object with open and closed chats by department', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/dashboards/charts/timings?start=2019-10-25T15:08:17.248Z&end=2019-12-08T15:08:17.248Z'))
.set(credentials)
@ -258,11 +606,52 @@ describe('LIVECHAT - dashboards', function () {
expect(res.body.chatDuration).to.have.property('longest');
});
});
(IS_EE ? it : it.skip)('should return data with correct values', async () => {
const start = moment().subtract(1, 'days').toISOString();
const end = moment().toISOString();
const result = await request
.get(api('livechat/analytics/dashboards/charts/timings'))
.query({ start, end, departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(result.body).to.have.property('success', true);
// const expected = {
// response: { avg: 0, longest: 0.207 }, // avg between delayBetweenMessage.min and delayBetweenMessage.max
// reaction: { avg: 0, longest: 0.221 }, // avg between delayBetweenMessage.min and delayBetweenMessage.max
// chatDuration: { avg: 0, longest: 0.18 }, // avg should be about avgClosedRoomChatDuration, and longest should be greater than avgClosedRoomChatDuration and within delta of 20
// success: true,
// };
const maxChatDuration = (DELAY_BETWEEN_MESSAGES.max * TOTAL_MESSAGES.max) / 1000;
const responseValues = result.body.response;
expect(responseValues).to.have.property('avg');
expect(responseValues).to.have.property('longest');
expect(responseValues.avg).to.be.closeTo((DELAY_BETWEEN_MESSAGES.min + DELAY_BETWEEN_MESSAGES.max) / 2000, 5);
expect(responseValues.longest).to.be.lessThan(maxChatDuration);
const reactionValues = result.body.reaction;
expect(reactionValues).to.have.property('avg');
expect(reactionValues).to.have.property('longest');
expect(reactionValues.avg).to.be.closeTo((DELAY_BETWEEN_MESSAGES.min + DELAY_BETWEEN_MESSAGES.max) / 2000, 5);
expect(reactionValues.longest).to.be.lessThan(maxChatDuration);
const chatDurationValues = result.body.chatDuration;
expect(chatDurationValues).to.have.property('avg');
expect(chatDurationValues).to.have.property('longest');
expect(chatDurationValues.avg).to.be.closeTo(avgClosedRoomChatDuration, 5);
expect(chatDurationValues.longest).to.be.greaterThan(avgClosedRoomChatDuration);
expect(chatDurationValues.longest).to.be.lessThan(avgClosedRoomChatDuration + 20);
});
});
describe('livechat/analytics/agent-overview', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/agent-overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: 'Total_conversations' })
@ -271,7 +660,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an "invalid-chart-name error" when the chart name is empty', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/agent-overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: '' })
@ -305,11 +694,37 @@ describe('LIVECHAT - dashboards', function () {
expect(result.body.head).to.be.an('array');
expect(result.body.data).to.be.an('array');
});
(IS_EE ? it : it.skip)('should return agent overview data with correct values', async () => {
const yesterday = moment().subtract(1, 'days').format('YYYY-MM-DD');
const today = moment().startOf('day').format('YYYY-MM-DD');
const result = await request
.get(api('livechat/analytics/agent-overview'))
.query({ from: yesterday, to: today, name: 'Total_conversations', departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(result.body).to.have.property('success', true);
expect(result.body).to.have.property('head');
expect(result.body).to.have.property('data');
expect(result.body.data).to.be.an('array');
expect(result.body.data).to.have.lengthOf(2);
const user1Data = result.body.data.find((data: any) => data.name === agents[0].user.username);
const user2Data = result.body.data.find((data: any) => data.name === agents[1].user.username);
expect(user1Data).to.not.be.undefined;
expect(user2Data).to.not.be.undefined;
expect(user1Data).to.have.property('value', '28.57%');
expect(user2Data).to.have.property('value', '71.43%');
});
});
describe('livechat/analytics/overview', () => {
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
await updatePermission('view-livechat-manager', []);
await removePermissionFromAllRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: 'Conversations' })
@ -318,7 +733,7 @@ describe('LIVECHAT - dashboards', function () {
.expect(403);
});
it('should return an "invalid-chart-name error" when the chart name is empty', async () => {
await updatePermission('view-livechat-manager', ['admin']);
await restorePermissionToRoles('view-livechat-manager');
await request
.get(api('livechat/analytics/overview'))
.query({ from: '2020-01-01', to: '2020-01-02', name: '' })
@ -351,5 +766,43 @@ describe('LIVECHAT - dashboards', function () {
expect(result.body[0]).to.have.property('title', 'Total_conversations');
expect(result.body[0]).to.have.property('value', 0);
});
(IS_EE ? it : it.skip)('should return analytics overview data with correct values', async () => {
const yesterday = moment().subtract(1, 'days').format('YYYY-MM-DD');
const today = moment().startOf('day').format('YYYY-MM-DD');
const result = await request
.get(api('livechat/analytics/overview'))
.query({ from: yesterday, to: today, name: 'Conversations', departmentId: department._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(result.body).to.be.an('array');
const expectedResult = [
{ title: 'Total_conversations', value: 7 },
{ title: 'Open_conversations', value: 4 },
{ title: 'On_Hold_conversations', value: 1 },
// { title: 'Total_messages', value: 6 },
// { title: 'Busiest_day', value: moment().format('dddd') }, // TODO: need to check y this return a day before
{ title: 'Conversations_per_day', value: '3.50' },
{ title: 'Busiest_time', value: '- -' },
];
expectedResult.forEach((expected) => {
const resultItem = result.body.find((item: any) => item.title === expected.title);
expect(resultItem).to.not.be.undefined;
expect(resultItem).to.have.property('value', expected.value);
});
const minMessages = TOTAL_MESSAGES.min * TOTAL_ROOMS;
const maxMessages = TOTAL_MESSAGES.max * TOTAL_ROOMS;
const totalMessages = result.body.find((item: any) => item.title === 'Total_messages');
expect(totalMessages).to.not.be.undefined;
const totalMessagesValue = parseInt(totalMessages.value);
expect(totalMessagesValue).to.be.greaterThanOrEqual(minMessages);
expect(totalMessagesValue).to.be.lessThanOrEqual(maxMessages);
});
});
});

View File

@ -38,6 +38,16 @@ export class NodeRandomGenerator extends RandomGenerator {
return result.substring(0, digits);
}
/**
* @name Random.between Returns a random integer between min and max, inclusive.
* @param min Minimum value (inclusive)
* @param max Maximum value (inclusive)
* @returns A random integer between min and max, inclusive.
*/
between(min: number, max: number) {
return Math.floor(this.fraction() * (max - min + 1)) + min;
}
protected safelyCreateWithSeeds(...seeds: readonly unknown[]) {
return new AleaRandomGenerator({ seeds });
}