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 1s
937 lines
34 KiB
TypeScript
937 lines
34 KiB
TypeScript
import { faker } from '@faker-js/faker';
|
|
import type {
|
|
ILivechatInquiryRecord,
|
|
ILivechatPriority,
|
|
IOmnichannelRoom,
|
|
IOmnichannelServiceLevelAgreements,
|
|
} from '@rocket.chat/core-typings';
|
|
import { OmnichannelSortingMechanismSettingType } from '@rocket.chat/core-typings';
|
|
import { expect } from 'chai';
|
|
import { after, before, describe, it } from 'mocha';
|
|
|
|
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
|
import { createDepartmentWithAnOnlineAgent } from '../../../data/livechat/department';
|
|
import { fetchAllInquiries } from '../../../data/livechat/inquiries';
|
|
import { createSLA, deleteSLA, bulkCreateSLA, deleteAllSLA } from '../../../data/livechat/priorities';
|
|
import {
|
|
createAgent,
|
|
createVisitor,
|
|
createLivechatRoom,
|
|
bulkCreateLivechatRooms,
|
|
startANewLivechatRoomAndTakeIt,
|
|
makeAgentAvailable,
|
|
deleteAgent,
|
|
} from '../../../data/livechat/rooms';
|
|
import {
|
|
addPermissions,
|
|
removePermissionFromAllRoles,
|
|
removePermissions,
|
|
restorePermissionToRoles,
|
|
updateEESetting,
|
|
updatePermission,
|
|
updateSetting,
|
|
} from '../../../data/permissions.helper';
|
|
import { IS_EE } from '../../../e2e/config/constants';
|
|
import { generateRandomSLAData } from '../../../e2e/utils/omnichannel/sla';
|
|
|
|
(IS_EE ? describe : describe.skip)('[EE] LIVECHAT - Priorities & SLAs', () => {
|
|
before((done) => getCredentials(done));
|
|
|
|
before(async () => {
|
|
await updateSetting('Livechat_enabled', true);
|
|
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
|
|
await updateEESetting('Livechat_Require_Contact_Verification', 'never');
|
|
});
|
|
|
|
after(async () => {
|
|
await addPermissions({
|
|
'manage-livechat-priorities': ['admin', 'livechat-manager'],
|
|
'manage-livechat-sla': ['admin', 'livechat-manager'],
|
|
'view-l-room': ['admin', 'livechat-manager', 'livechat-agent'],
|
|
});
|
|
});
|
|
|
|
describe('livechat/sla', () => {
|
|
// GET
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission for [GET] livechat/sla endpoint', async () => {
|
|
await removePermissions(['manage-livechat-sla', 'view-l-room']);
|
|
const response = await request.get(api('livechat/sla')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should return an array of slas', async () => {
|
|
await addPermissions({
|
|
'manage-livechat-sla': ['admin', 'livechat-manager'],
|
|
'view-l-room': ['livechat-agent', 'admin', 'livechat-manager'],
|
|
});
|
|
const sla = await createSLA();
|
|
expect(sla).to.not.be.undefined;
|
|
expect(sla).to.have.property('_id');
|
|
const response = await request.get(api('livechat/sla')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.sla).to.be.an('array').with.lengthOf.greaterThan(0);
|
|
const current = response.body.sla.find((p: IOmnichannelServiceLevelAgreements) => p && p?._id === sla._id);
|
|
expect(current).to.be.an('object');
|
|
expect(current).to.have.property('name', sla.name);
|
|
expect(current).to.have.property('description', sla.description);
|
|
expect(current).to.have.property('dueTimeInMinutes', sla.dueTimeInMinutes);
|
|
await deleteSLA(sla._id);
|
|
});
|
|
// POST
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission for [POST] livechat/sla endpoint', async () => {
|
|
await updatePermission('manage-livechat-sla', []);
|
|
const response = await request
|
|
.post(api('livechat/sla'))
|
|
.set(credentials)
|
|
.send(generateRandomSLAData())
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should create a new sla', async () => {
|
|
await restorePermissionToRoles('manage-livechat-sla');
|
|
|
|
const sla = generateRandomSLAData();
|
|
|
|
const response = await request
|
|
.post(api('livechat/sla'))
|
|
.set(credentials)
|
|
.send(sla)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.sla).to.be.an('object');
|
|
expect(response.body.sla).to.have.property('_id');
|
|
expect(response.body.sla).to.have.property('name', sla.name);
|
|
expect(response.body.sla).to.have.property('description', sla.description);
|
|
expect(response.body.sla).to.have.property('dueTimeInMinutes', sla.dueTimeInMinutes);
|
|
|
|
await deleteSLA(response.body.sla._id);
|
|
});
|
|
it('should throw an error when trying to create a duplicate sla with same dueTimeInMinutes', async () => {
|
|
const firstSla = generateRandomSLAData();
|
|
|
|
const response = await request
|
|
.post(api('livechat/sla'))
|
|
.set(credentials)
|
|
.send(firstSla)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(response.body).to.have.property('success', true);
|
|
|
|
const secondSla = generateRandomSLAData();
|
|
|
|
secondSla.dueTimeInMinutes = firstSla.dueTimeInMinutes;
|
|
|
|
const secondResponse = await request
|
|
.post(api('livechat/sla'))
|
|
.set(credentials)
|
|
.send(secondSla)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(secondResponse.body).to.have.property('success', false);
|
|
expect(secondResponse.body).to.have.property('error');
|
|
expect(secondResponse.body?.error).to.contain('error-duplicated-sla');
|
|
|
|
await deleteSLA(response.body.sla._id);
|
|
});
|
|
it('should throw an error when trying to create a duplicate sla with same name', async () => {
|
|
const firstSla = generateRandomSLAData();
|
|
|
|
const response = await request
|
|
.post(api('livechat/sla'))
|
|
.set(credentials)
|
|
.send(firstSla)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(response.body).to.have.property('success', true);
|
|
|
|
const secondSla = generateRandomSLAData();
|
|
|
|
secondSla.name = firstSla.name;
|
|
|
|
const secondResponse = await request
|
|
.post(api('livechat/sla'))
|
|
.set(credentials)
|
|
.send(secondSla)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(secondResponse.body).to.have.property('success', false);
|
|
expect(secondResponse.body).to.have.property('error');
|
|
expect(secondResponse.body?.error).to.contain('error-duplicated-sla');
|
|
|
|
await deleteSLA(response.body.sla._id);
|
|
});
|
|
});
|
|
|
|
describe('livechat/sla/:slaId', () => {
|
|
// GET
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission for [GET] livechat/sla/:slaId endpoint', async () => {
|
|
await Promise.all([removePermissionFromAllRoles('manage-livechat-sla'), removePermissionFromAllRoles('view-l-room')]);
|
|
|
|
const response = await request.get(api('livechat/sla/123')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should create, find and delete an sla', async () => {
|
|
await Promise.all([restorePermissionToRoles('manage-livechat-sla'), restorePermissionToRoles('view-l-room')]);
|
|
|
|
const sla = await createSLA();
|
|
const response = await request
|
|
.get(api(`livechat/sla/${sla._id}`))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body).to.be.an('object');
|
|
expect(response.body._id).to.be.equal(sla._id);
|
|
await deleteSLA(sla._id);
|
|
});
|
|
// PUT
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission for [PUT] livechat/sla/:slaId endpoint', async () => {
|
|
await removePermissionFromAllRoles('manage-livechat-sla');
|
|
|
|
const response = await request
|
|
.put(api('livechat/sla/123'))
|
|
.set(credentials)
|
|
.send(generateRandomSLAData())
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(403);
|
|
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should update an sla', async () => {
|
|
await restorePermissionToRoles('manage-livechat-sla');
|
|
|
|
const sla = await createSLA();
|
|
const newSlaData = generateRandomSLAData();
|
|
|
|
const response = await request
|
|
.put(api(`livechat/sla/${sla._id}`))
|
|
.set(credentials)
|
|
.send(newSlaData)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.sla).to.be.an('object');
|
|
expect(response.body.sla).to.have.property('_id');
|
|
expect(response.body.sla).to.have.property('name', newSlaData.name);
|
|
expect(response.body.sla).to.have.property('description', newSlaData.description);
|
|
expect(response.body.sla).to.have.property('dueTimeInMinutes', newSlaData.dueTimeInMinutes);
|
|
|
|
await deleteSLA(response.body.sla._id);
|
|
});
|
|
it('should throw an error when trying to update a sla with a duplicate name', async () => {
|
|
const firstSla = await createSLA();
|
|
const secondSla = await createSLA();
|
|
|
|
secondSla.name = firstSla.name;
|
|
|
|
const response = await request
|
|
.put(api(`livechat/sla/${secondSla._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
name: secondSla.name,
|
|
description: secondSla.description,
|
|
dueTimeInMinutes: secondSla.dueTimeInMinutes,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error');
|
|
expect(response.body?.error).to.contain('error-duplicated-sla');
|
|
|
|
await deleteSLA(firstSla._id);
|
|
await deleteSLA(secondSla._id);
|
|
});
|
|
it('should throw an error when trying to update a sla with a duplicate dueTimeInMinutes', async () => {
|
|
const firstSla = await createSLA();
|
|
const secondSla = await createSLA();
|
|
|
|
secondSla.dueTimeInMinutes = firstSla.dueTimeInMinutes;
|
|
|
|
const response = await request
|
|
.put(api(`livechat/sla/${secondSla._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
name: secondSla.name,
|
|
description: secondSla.description,
|
|
dueTimeInMinutes: secondSla.dueTimeInMinutes,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error');
|
|
expect(response.body?.error).to.contain('error-duplicated-sla');
|
|
|
|
await deleteSLA(firstSla._id);
|
|
await deleteSLA(secondSla._id);
|
|
});
|
|
|
|
// DELETE
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission for [DELETE] livechat/sla/:slaId endpoint', async () => {
|
|
await updatePermission('manage-livechat-sla', []);
|
|
const response = await request
|
|
.delete(api('livechat/sla/123'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should delete an sla', async () => {
|
|
await updatePermission('manage-livechat-sla', ['admin', 'livechat-manager']);
|
|
const sla = await createSLA();
|
|
const response = await request
|
|
.delete(api(`livechat/sla/${sla._id}`))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
describe('livechat/inquiry.setSLA', () => {
|
|
before(async () => {
|
|
await createAgent();
|
|
await makeAgentAvailable();
|
|
});
|
|
after(async () => {
|
|
await deleteAgent();
|
|
});
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
|
await removePermissions(['manage-livechat-sla', 'view-l-room', 'manage-livechat-priorities']);
|
|
const response = await request
|
|
.put(api('livechat/inquiry.setSLA'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: '123',
|
|
sla: '123',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should fail if roomId is not in request body', async () => {
|
|
await addPermissions({
|
|
'manage-livechat-sla': ['admin', 'livechat-manager'],
|
|
'manage-livechat-priorities': ['admin', 'livechat-manager'],
|
|
'view-l-room': ['livechat-agent', 'admin', 'livechat-manager'],
|
|
});
|
|
const response = await request
|
|
.put(api('livechat/inquiry.setSLA'))
|
|
.set(credentials)
|
|
.send({
|
|
sla: '123',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should fail if roomId is invalid', async () => {
|
|
const response = await request
|
|
.put(api('livechat/inquiry.setSLA'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: '123',
|
|
sla: '123',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should fail if sla is not in request body', async () => {
|
|
const response = await request
|
|
.put(api('livechat/inquiry.setSLA'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: '123',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should fail if sla is not valid', async () => {
|
|
const visitor = await createVisitor();
|
|
const room = await createLivechatRoom(visitor.token);
|
|
|
|
const response = await request
|
|
.put(api('livechat/inquiry.setSLA'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: room._id,
|
|
sla: '123',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should fail if inquiry is not queued', async () => {
|
|
const {
|
|
room: { _id: roomId },
|
|
} = await startANewLivechatRoomAndTakeIt();
|
|
|
|
const response = await request
|
|
.put(api('livechat/inquiry.setSLA'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId,
|
|
sla: '123',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should prioritize an inquiry', async () => {
|
|
const visitor = await createVisitor();
|
|
const room = await createLivechatRoom(visitor.token);
|
|
const sla = await createSLA();
|
|
const response = await request
|
|
.put(api('livechat/inquiry.setSLA'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: room._id,
|
|
sla: sla._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
describe('livechat/priorities', () => {
|
|
let priority: ILivechatPriority;
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
|
await removePermissions(['manage-livechat-priorities', 'view-l-room']);
|
|
const response = await request
|
|
.get(api('livechat/priorities'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should return an array of priorities', async () => {
|
|
await addPermissions({
|
|
'manage-livechat-priorities': ['admin', 'livechat-manager'],
|
|
'view-l-room': ['livechat-agent', 'admin', 'livechat-manager'],
|
|
});
|
|
const response = await request
|
|
.get(api('livechat/priorities'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.priorities).to.be.an('array');
|
|
expect(response.body.priorities).to.have.lengthOf(5);
|
|
expect(response.body.priorities[0]).to.have.property('_id');
|
|
expect(response.body.priorities[0]).to.have.property('i18n');
|
|
expect(response.body.priorities[0]).to.have.property('dirty');
|
|
priority = response.body.priorities[0];
|
|
});
|
|
it('should return an array of priorities matching text param', async () => {
|
|
const response = await request
|
|
.get(api('livechat/priorities'))
|
|
.set(credentials)
|
|
.query({
|
|
text: priority.name,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.priorities).to.be.an('array');
|
|
expect(response.body.priorities).to.have.length.greaterThan(0);
|
|
expect(response.body.priorities[0]).to.have.property('_id');
|
|
});
|
|
});
|
|
|
|
describe('livechat/priorities/:priorityId', () => {
|
|
let priority: ILivechatPriority;
|
|
const name = `${faker.lorem.word()}${faker.git.commitSha({ length: 6 })}`;
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
|
await removePermissions(['manage-livechat-priorities', 'view-l-room']);
|
|
const response = await request.get(api('livechat/priorities/123')).set(credentials).expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error');
|
|
expect(response.body?.error).to.contain('error-unauthorized');
|
|
});
|
|
it('should return a priority by id', async () => {
|
|
await addPermissions({
|
|
'manage-livechat-priorities': ['admin', 'livechat-manager'],
|
|
'view-l-room': ['livechat-agent', 'admin', 'livechat-manager'],
|
|
});
|
|
const {
|
|
body: { priorities },
|
|
} = await request.get(api('livechat/priorities')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
priority = priorities[0];
|
|
|
|
const response = await request
|
|
.get(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body).to.be.an('object');
|
|
expect(response.body._id).to.be.equal(priority._id);
|
|
});
|
|
it('should throw 404 when priority does not exist', async () => {
|
|
const response = await request.get(api('livechat/priorities/123')).set(credentials).expect(404);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should update a priority when using PUT', async () => {
|
|
const response = await request
|
|
.put(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
name,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
});
|
|
it('should return dirty: true after a priority has been updated', async () => {
|
|
const response = await request
|
|
.get(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body).to.have.property('dirty', true);
|
|
});
|
|
it('should return an array of priorities matching text param', async () => {
|
|
const response = await request
|
|
.get(api('livechat/priorities'))
|
|
.set(credentials)
|
|
.query({
|
|
text: name,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.priorities).to.be.an('array');
|
|
expect(response.body.priorities).to.have.length.greaterThan(0);
|
|
const pos = response.body.priorities.findIndex((p: ILivechatPriority) => p._id === priority._id);
|
|
expect(pos).to.be.greaterThan(-1);
|
|
expect(response.body.priorities[pos]).to.have.property('_id');
|
|
expect(response.body.priorities[pos]).to.have.property('i18n', priority.i18n);
|
|
});
|
|
it('should edit a priority with a PUT', async () => {
|
|
const newName = `${faker.lorem.word()}${faker.git.commitSha({ length: 6 })}`;
|
|
const response = await request
|
|
.put(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
name: newName,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
const newPriorityResponse = await request
|
|
.get(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(newPriorityResponse.body).to.have.property('success', true);
|
|
expect(newPriorityResponse.body).to.have.property('dirty', true);
|
|
expect(newPriorityResponse.body).to.have.property('name', newName);
|
|
});
|
|
it('should fail to edit a priority with a PUT if using too many parameters', async () => {
|
|
const newName = faker.lorem.word();
|
|
const response = await request
|
|
.put(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
name: newName,
|
|
reset: true,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error');
|
|
expect(response.body?.error).to.contain('invalid-params');
|
|
});
|
|
it('should fail to edit a priority with a PUT if using an object as name', async () => {
|
|
const newName = faker.lorem.word();
|
|
const response = await request
|
|
.put(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
name: { name: newName },
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error');
|
|
expect(response.body?.error).to.contain('invalid-params');
|
|
});
|
|
it('should not fail to edit a priority with a PUT if using a boolean as name (it becomes a string)', async () => {
|
|
const response = await request
|
|
.put(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
name: false,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
});
|
|
it('should fail to update a non-existing priority', async () => {
|
|
const response = await request
|
|
.put(api('livechat/priorities/123'))
|
|
.set(credentials)
|
|
.send({
|
|
name: faker.lorem.word(),
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
});
|
|
it('should reset a single priority with a reset:true PUT parameter', async () => {
|
|
const response = await request
|
|
.put(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
reset: true,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
const newPriorityResponse = await request
|
|
.get(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(newPriorityResponse.body).to.have.property('success', true);
|
|
expect(newPriorityResponse.body).to.have.property('dirty', false);
|
|
expect(newPriorityResponse.body).to.not.have.property('name');
|
|
});
|
|
it('should throw a duplicate error incase there is already a priority with same name', async () => {
|
|
const {
|
|
body: { priorities },
|
|
} = await request.get(api('livechat/priorities')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
// change name of first priority to a random name
|
|
const newName = `${faker.lorem.word()}${faker.git.commitSha({ length: 6 })}`;
|
|
await request
|
|
.put(api(`livechat/priorities/${priorities[0]._id}`))
|
|
.set(credentials)
|
|
.send({ name: newName })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
// change name of second priority to the name of first priority and expect error
|
|
const response = await request
|
|
.put(api(`livechat/priorities/${priorities[1]._id}`))
|
|
.set(credentials)
|
|
.send({ name: newName })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error', 'error-duplicate-priority-name');
|
|
});
|
|
it('should throw a duplicate error incase there is already a priority with same name (case insensitive)', async () => {
|
|
const {
|
|
body: { priorities },
|
|
} = await request.get(api('livechat/priorities')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
// change name of first priority to a random name
|
|
const newNameLowercase = `${faker.lorem.word()}${faker.git.commitSha({ length: 6 })}`.toLowerCase();
|
|
await request
|
|
.put(api(`livechat/priorities/${priorities[0]._id}`))
|
|
.set(credentials)
|
|
.send({ name: newNameLowercase })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
// change name of second priority to the name of first priority in different case and expect error
|
|
const response = await request
|
|
.put(api(`livechat/priorities/${priorities[1]._id}`))
|
|
.set(credentials)
|
|
.send({ name: newNameLowercase.toUpperCase() })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error', 'error-duplicate-priority-name');
|
|
});
|
|
});
|
|
|
|
describe('livechat/priorities.reset', () => {
|
|
let priority: ILivechatPriority;
|
|
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
|
await updatePermission('manage-livechat-priorities', []);
|
|
const response = await request.post(api('livechat/priorities.reset')).set(credentials).expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error');
|
|
expect(response.body?.error).to.contain('error-unauthorized');
|
|
});
|
|
it('should respond reset:true when a priority has been changed', async () => {
|
|
await updatePermission('manage-livechat-priorities', ['admin', 'livechat-manager']);
|
|
const {
|
|
body: { priorities },
|
|
} = await request.get(api('livechat/priorities')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
|
|
|
priority = priorities[0];
|
|
|
|
priority.name = `${faker.lorem.word()}${faker.git.commitSha({ length: 6 })}`;
|
|
const responseChange = await request
|
|
.put(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.send({
|
|
name: priority.name,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(responseChange.body).to.have.property('success', true);
|
|
|
|
const response = await request
|
|
.get(api('livechat/priorities.reset'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body).to.have.property('reset', true);
|
|
});
|
|
it('should reset all priorities', async () => {
|
|
const resetRespose = await request
|
|
.post(api('livechat/priorities.reset'))
|
|
.set(credentials)
|
|
.send()
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(resetRespose.body).to.have.property('success', true);
|
|
});
|
|
it('should return reset: false after all priorities have been reset', async () => {
|
|
const response = await request
|
|
.get(api('livechat/priorities.reset'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body).to.have.property('reset', false);
|
|
});
|
|
it('should change the priority name and dirty status when reset', async () => {
|
|
const response = await request
|
|
.get(api(`livechat/priorities/${priority._id}`))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body).to.have.property('dirty', false);
|
|
expect(response.body).to.not.have.property('name');
|
|
});
|
|
it('should change all priorities to their default', async () => {
|
|
const response = await request
|
|
.get(api('livechat/priorities'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
expect(response.body).to.have.property('success', true);
|
|
expect(response.body.priorities).to.be.an('array');
|
|
response.body.priorities.forEach((priority: ILivechatPriority) => {
|
|
expect(priority).to.not.have.property('name');
|
|
expect(priority).to.have.property('dirty', false);
|
|
});
|
|
});
|
|
it('should fail to reset when lacking permissions', async () => {
|
|
await updatePermission('manage-livechat-priorities', []);
|
|
const response = await request.post(api('livechat/priorities.reset')).set(credentials).expect(403);
|
|
expect(response.body).to.have.property('success', false);
|
|
expect(response.body).to.have.property('error');
|
|
expect(response.body?.error).to.contain('error-unauthorized');
|
|
});
|
|
});
|
|
|
|
describe('Inquiry queue sorting mechanism', () => {
|
|
let omniRooms: IOmnichannelRoom[];
|
|
let departmentWithAgent: Awaited<ReturnType<typeof createDepartmentWithAnOnlineAgent>>;
|
|
let priorities: ILivechatPriority[];
|
|
let slas: Awaited<ReturnType<typeof bulkCreateSLA>>;
|
|
|
|
const hasSlaProps = (inquiry: ILivechatInquiryRecord): inquiry is ILivechatInquiryRecord & { slaId: string } =>
|
|
!!inquiry.estimatedWaitingTimeQueue && !!inquiry.slaId;
|
|
const hasPriorityProps = (inquiry: ILivechatInquiryRecord): inquiry is ILivechatInquiryRecord & { priorityId: string } =>
|
|
inquiry.priorityId !== undefined;
|
|
|
|
const getPriorityOrderById = (id: string) => {
|
|
const priority = priorities.find((priority) => priority._id === id);
|
|
return priority?.sortItem || 0;
|
|
};
|
|
|
|
const sortBySLAProps = (inquiry1: ILivechatInquiryRecord, inquiry2: ILivechatInquiryRecord): number => {
|
|
if (hasSlaProps(inquiry1) || hasSlaProps(inquiry2)) {
|
|
if (hasSlaProps(inquiry1) && hasSlaProps(inquiry2)) {
|
|
// if both inquiries have sla props, then sort by estimatedWaitingTimeQueue: 1
|
|
const estimatedWaitingTimeQueue1 = inquiry1.estimatedWaitingTimeQueue;
|
|
const estimatedWaitingTimeQueue2 = inquiry2.estimatedWaitingTimeQueue;
|
|
|
|
if (estimatedWaitingTimeQueue1 !== estimatedWaitingTimeQueue2) {
|
|
return estimatedWaitingTimeQueue1 - estimatedWaitingTimeQueue2;
|
|
}
|
|
}
|
|
|
|
if (hasSlaProps(inquiry1)) {
|
|
return -1;
|
|
}
|
|
|
|
if (hasSlaProps(inquiry2)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
const sortByPriorityProps = (inquiry1: ILivechatInquiryRecord, inquiry2: ILivechatInquiryRecord): number => {
|
|
if (hasPriorityProps(inquiry1) || hasPriorityProps(inquiry2)) {
|
|
if (hasPriorityProps(inquiry1) && hasPriorityProps(inquiry2)) {
|
|
// if both inquiries have priority props, then sort by priorityId: 1
|
|
return getPriorityOrderById(inquiry1.priorityId) - getPriorityOrderById(inquiry2.priorityId);
|
|
}
|
|
|
|
if (hasPriorityProps(inquiry1)) {
|
|
return -1;
|
|
}
|
|
|
|
if (hasPriorityProps(inquiry2)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
// this should sort using logic - { ts: 1 }
|
|
const sortByTimestamp = (inquiry1: ILivechatInquiryRecord, inquiry2: ILivechatInquiryRecord) => {
|
|
return new Date(inquiry1.ts).getTime() - new Date(inquiry2.ts).getTime();
|
|
};
|
|
|
|
// this should sort using logic - { priorityWeight: 1, ts: 1 }
|
|
const sortByPriority = (inquiry1: ILivechatInquiryRecord, inquiry2: ILivechatInquiryRecord) => {
|
|
const priorityPropsSort = sortByPriorityProps(inquiry1, inquiry2);
|
|
if (priorityPropsSort !== 0) {
|
|
return priorityPropsSort;
|
|
}
|
|
|
|
return sortByTimestamp(inquiry1, inquiry2);
|
|
};
|
|
|
|
// this should sort using logic - { estimatedWaitingTimeQueue: 1, ts: 1 }
|
|
const sortBySLA = (inquiry1: ILivechatInquiryRecord, inquiry2: ILivechatInquiryRecord) => {
|
|
const slaPropsSort = sortBySLAProps(inquiry1, inquiry2);
|
|
if (slaPropsSort !== 0) {
|
|
return slaPropsSort;
|
|
}
|
|
|
|
return sortByTimestamp(inquiry1, inquiry2);
|
|
};
|
|
|
|
const filterUnnecessaryProps = (inquiries: ILivechatInquiryRecord[]) =>
|
|
inquiries.map((inquiry) => {
|
|
return {
|
|
_id: inquiry._id,
|
|
rid: inquiry.rid,
|
|
...(hasPriorityProps(inquiry) && {
|
|
priority: {
|
|
priorityWeight: getPriorityOrderById(inquiry.priorityId),
|
|
id: inquiry.priorityWeight,
|
|
},
|
|
}),
|
|
...(hasSlaProps(inquiry) && {
|
|
sla: {
|
|
estimatedWaitingTimeQueue: inquiry.estimatedWaitingTimeQueue,
|
|
},
|
|
}),
|
|
ts: inquiry.ts,
|
|
};
|
|
});
|
|
|
|
const compareInquiries = (inquiries1: ILivechatInquiryRecord[], inquiries2: ILivechatInquiryRecord[]) => {
|
|
const filteredInquiries1 = filterUnnecessaryProps(inquiries1);
|
|
const filteredInquiries2 = filterUnnecessaryProps(inquiries2);
|
|
|
|
expect(filteredInquiries1).to.deep.equal(filteredInquiries2);
|
|
};
|
|
|
|
it('it should create all the data required for further testing', async () => {
|
|
departmentWithAgent = await createDepartmentWithAnOnlineAgent();
|
|
|
|
const {
|
|
body: { priorities: prioritiesResponse },
|
|
} = (await request.get(api('livechat/priorities')).set(credentials).expect('Content-Type', 'application/json').expect(200)) as {
|
|
body: { priorities: ILivechatPriority[] };
|
|
};
|
|
priorities = prioritiesResponse;
|
|
|
|
await deleteAllSLA();
|
|
slas = await bulkCreateSLA(5);
|
|
|
|
type RoomParamsReturnType = { priority: string } | { sla: string } | { priority: string; sla: string } | undefined;
|
|
|
|
// create 20 rooms, 5 with only priority, 5 with only SLA, 5 with both, 5 without priority or SLA
|
|
omniRooms = await bulkCreateLivechatRooms(20, departmentWithAgent.department._id, (index): RoomParamsReturnType => {
|
|
if (index < 5) {
|
|
return {
|
|
priority: priorities[index]._id,
|
|
};
|
|
}
|
|
|
|
if (index < 10) {
|
|
return {
|
|
sla: slas[index - 5]._id,
|
|
};
|
|
}
|
|
|
|
if (index < 15) {
|
|
// random number between 0 and 4
|
|
const randomPriorityIndex = Math.floor(Math.random() * 5);
|
|
const randomSlaIndex = Math.floor(Math.random() * 5);
|
|
|
|
return {
|
|
priority: priorities[randomPriorityIndex]._id,
|
|
sla: slas[randomSlaIndex]._id,
|
|
};
|
|
}
|
|
});
|
|
});
|
|
|
|
it('it should sort the queue based on priority', async () => {
|
|
await updateEESetting('Omnichannel_sorting_mechanism', OmnichannelSortingMechanismSettingType.Priority);
|
|
|
|
const origInquiries = await fetchAllInquiries(departmentWithAgent.agent.credentials, departmentWithAgent.department._id);
|
|
expect(origInquiries.length).to.be.greaterThanOrEqual(omniRooms.length);
|
|
|
|
const expectedSortedInquiries: ILivechatInquiryRecord[] = JSON.parse(JSON.stringify(origInquiries));
|
|
|
|
expectedSortedInquiries.sort(sortByPriority);
|
|
|
|
compareInquiries(origInquiries, expectedSortedInquiries);
|
|
});
|
|
|
|
it('it should sort the queue based on slas', async () => {
|
|
await updateEESetting('Omnichannel_sorting_mechanism', OmnichannelSortingMechanismSettingType.SLAs);
|
|
|
|
const origInquiries = await fetchAllInquiries(departmentWithAgent.agent.credentials, departmentWithAgent.department._id);
|
|
expect(origInquiries.length).to.be.greaterThanOrEqual(omniRooms.length);
|
|
|
|
const expectedSortedInquiries = origInquiries.sort(sortBySLA);
|
|
|
|
compareInquiries(origInquiries, expectedSortedInquiries);
|
|
});
|
|
|
|
it('it should sort the queue based on timestamp', async () => {
|
|
await updateEESetting('Omnichannel_sorting_mechanism', OmnichannelSortingMechanismSettingType.Timestamp);
|
|
|
|
const origInquiries = await fetchAllInquiries(departmentWithAgent.agent.credentials, departmentWithAgent.department._id);
|
|
expect(origInquiries.length).to.be.greaterThanOrEqual(omniRooms.length);
|
|
|
|
const expectedSortedInquiries = origInquiries.sort(sortByTimestamp);
|
|
|
|
compareInquiries(origInquiries, expectedSortedInquiries);
|
|
});
|
|
});
|
|
});
|