snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
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
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
This commit is contained in:
3796
apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts
Normal file
3796
apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts
Normal file
File diff suppressed because it is too large
Load Diff
788
apps/meteor/tests/end-to-end/api/livechat/01-agents.ts
Normal file
788
apps/meteor/tests/end-to-end/api/livechat/01-agents.ts
Normal file
@@ -0,0 +1,788 @@
|
||||
import type { Credentials } from '@rocket.chat/api-client';
|
||||
import { UserStatus } from '@rocket.chat/core-typings';
|
||||
import type { ILivechatVisitor, IOmnichannelRoom, ILivechatAgent, ILivechatDepartment, IRoom, IUser } from '@rocket.chat/core-typings';
|
||||
import { Random } from '@rocket.chat/random';
|
||||
import { expect } from 'chai';
|
||||
import { after, before, describe, it } from 'mocha';
|
||||
import type { Response } from 'supertest';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { disableDefaultBusinessHour, makeDefaultBusinessHourActiveAndClosed } from '../../../data/livechat/businessHours';
|
||||
import { createDepartment, deleteDepartment } from '../../../data/livechat/department';
|
||||
import {
|
||||
createAgent,
|
||||
createManager,
|
||||
createVisitor,
|
||||
createLivechatRoom,
|
||||
takeInquiry,
|
||||
fetchInquiry,
|
||||
makeAgentAvailable,
|
||||
startANewLivechatRoomAndTakeIt,
|
||||
moveBackToQueue,
|
||||
closeOmnichannelRoom,
|
||||
deleteVisitor,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import {
|
||||
removePermissionFromAllRoles,
|
||||
restorePermissionToRoles,
|
||||
updateEESetting,
|
||||
updatePermission,
|
||||
updateSetting,
|
||||
} from '../../../data/permissions.helper';
|
||||
import { password } from '../../../data/user';
|
||||
import { createUser, deleteUser, getMe, login, setUserStatus } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
describe('LIVECHAT - Agents', () => {
|
||||
let agent: ILivechatAgent;
|
||||
let manager: ILivechatAgent;
|
||||
|
||||
let agent2: { user: IUser; credentials: Credentials };
|
||||
|
||||
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');
|
||||
await updateSetting('Omnichannel_enable_department_removal', true);
|
||||
agent = await createAgent();
|
||||
manager = await createManager();
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
const userCredentials = await login(user.username, password);
|
||||
await createAgent(user.username);
|
||||
|
||||
await makeAgentAvailable(userCredentials);
|
||||
|
||||
agent2 = {
|
||||
user,
|
||||
credentials: userCredentials,
|
||||
};
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Omnichannel_enable_department_removal', false);
|
||||
await deleteUser(agent2.user);
|
||||
});
|
||||
|
||||
// TODO: missing test cases for POST method
|
||||
describe('GET livechat/users/:type', () => {
|
||||
describe('with no permissions', () => {
|
||||
before(async () => {
|
||||
await updatePermission('edit-omnichannel-contact', []);
|
||||
await updatePermission('transfer-livechat-guest', []);
|
||||
await updatePermission('manage-livechat-agents', []);
|
||||
});
|
||||
after(async () => {
|
||||
await updatePermission('edit-omnichannel-contact', ['admin']);
|
||||
await updatePermission('transfer-livechat-guest', ['admin']);
|
||||
await updatePermission('manage-livechat-agents', ['admin']);
|
||||
});
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await request.get(api('livechat/users/agent')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
});
|
||||
});
|
||||
it('should throw an error when the type is invalid', async () => {
|
||||
await request
|
||||
.get(api('livechat/users/invalid-type'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body.error).to.be.equal('Invalid type');
|
||||
});
|
||||
});
|
||||
it('should return an array of agents', async () => {
|
||||
await request
|
||||
.get(api('livechat/users/agent'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.users).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
const agentRecentlyCreated = (res.body.users as ILivechatAgent[]).find((user) => agent._id === user._id);
|
||||
expect(agentRecentlyCreated?._id).to.be.equal(agent._id);
|
||||
});
|
||||
});
|
||||
it('should return an array of available agents', async () => {
|
||||
await request
|
||||
.get(api('livechat/users/agent'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.query({ onlyAvailable: true })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.users).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
expect(res.body.users.every((u: { statusLivechat: string }) => u.statusLivechat === 'available')).to.be.true;
|
||||
});
|
||||
});
|
||||
it('should return an array of available/unavailable agents when onlyAvailable is false', async () => {
|
||||
await request
|
||||
.get(api('livechat/users/agent'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.query({ onlyAvailable: false })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.users).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
expect(
|
||||
res.body.users.every(
|
||||
(u: { statusLivechat: string }) => !u.statusLivechat || ['available', 'not-available'].includes(u.statusLivechat),
|
||||
),
|
||||
).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return offline agents when showIdleAgents is true', async () => {
|
||||
await setUserStatus(agent2.credentials, UserStatus.OFFLINE);
|
||||
await request
|
||||
.get(api('livechat/users/agent'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.query({ showIdleAgents: true })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.users).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
expect(
|
||||
res.body.users.every(
|
||||
(u: { status: UserStatus }) =>
|
||||
!u.status || [UserStatus.ONLINE, UserStatus.OFFLINE, UserStatus.AWAY, UserStatus.BUSY].includes(u.status),
|
||||
),
|
||||
).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return only online agents when showIdleAgents is false', async () => {
|
||||
await setUserStatus(agent2.credentials, UserStatus.ONLINE);
|
||||
await request
|
||||
.get(api('livechat/users/agent'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.query({ showIdleAgents: false })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.users).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
expect(res.body.users.every((u: { status: UserStatus }) => u.status !== UserStatus.OFFLINE)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an array of managers', async () => {
|
||||
await request
|
||||
.get(api('livechat/users/manager'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.users).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
const managerRecentlyCreated = (res.body.users as ILivechatAgent[]).find((user) => manager._id === user._id);
|
||||
expect(managerRecentlyCreated?._id).to.be.equal(manager._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST livechat/users/:type', () => {
|
||||
describe('with no permission', () => {
|
||||
before(async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
});
|
||||
after(async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
});
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await request
|
||||
.post(api('livechat/users/agent'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
username: 'test-agent',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an error when type is invalid', async () => {
|
||||
await request
|
||||
.post(api('livechat/users/invalid-type'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
username: 'test-agent',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('should return an error when username is invalid', async () => {
|
||||
await request
|
||||
.post(api('livechat/users/agent'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
username: 'mr-not-valid',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('should return a valid user when all goes fine', async () => {
|
||||
const user = await createUser();
|
||||
await request
|
||||
.post(api('livechat/users/agent'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
username: user.username,
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('user');
|
||||
expect(res.body.user).to.have.property('_id');
|
||||
expect(res.body.user).to.have.property('username');
|
||||
});
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
|
||||
it('should properly create a manager', async () => {
|
||||
const user = await createUser();
|
||||
await request
|
||||
.post(api('livechat/users/manager'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
username: user.username,
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('user');
|
||||
expect(res.body.user).to.have.property('_id');
|
||||
expect(res.body.user).to.have.property('username');
|
||||
});
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET livechat/users/:type/:_id', () => {
|
||||
describe('with no permission', () => {
|
||||
before(async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
});
|
||||
after(async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
});
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await request
|
||||
.get(api(`livechat/users/agent/id${agent._id}`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with new user', () => {
|
||||
let user: IUser;
|
||||
before(async () => {
|
||||
user = await createUser();
|
||||
});
|
||||
after(async () => {
|
||||
await deleteUser(user);
|
||||
});
|
||||
it('should return an error when type is invalid', async () => {
|
||||
await request
|
||||
.get(api(`livechat/users/invalid-type/${agent._id}`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
}).timeout(5000);
|
||||
|
||||
it('should return a valid user when all goes fine', async () => {
|
||||
await request
|
||||
.get(api(`livechat/users/agent/${agent._id}`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('user');
|
||||
expect(res.body.user).to.have.property('_id');
|
||||
expect(res.body.user).to.have.property('username');
|
||||
expect(res.body.user).to.not.have.property('roles');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return { user: null } when user is not an agent', async () => {
|
||||
await request
|
||||
.get(api(`livechat/users/agent/${user._id}`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('user');
|
||||
expect(res.body.user).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return { user: null } when user is not a manager', async () => {
|
||||
await request
|
||||
.get(api(`livechat/users/manager/${user._id}`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('user');
|
||||
expect(res.body.user).to.be.null;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE livechat/users/:type/:_id', () => {
|
||||
describe('with no permission', () => {
|
||||
before(async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
});
|
||||
after(async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
});
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await request.delete(api(`livechat/users/agent/id`)).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
}).timeout(5000);
|
||||
});
|
||||
|
||||
it('should return an error when type is invalid', async () => {
|
||||
await request.delete(api(`livechat/users/invalid-type/id`)).set(credentials).expect('Content-Type', 'application/json').expect(400);
|
||||
}).timeout(5000);
|
||||
|
||||
it('should return an error when _id is invalid', async () => {
|
||||
await request.delete(api('livechat/users/agent/invalid-id')).set(credentials).expect('Content-Type', 'application/json').expect(400);
|
||||
}).timeout(5000);
|
||||
|
||||
it('should return a valid user when all goes fine', async () => {
|
||||
const agent = await createAgent();
|
||||
await request
|
||||
.delete(api(`livechat/users/agent/${agent._id}`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
(IS_EE ? describe : describe.skip)('livechat/agents/:agentId/departments', () => {
|
||||
let dep1: ILivechatDepartment;
|
||||
let dep2: ILivechatDepartment;
|
||||
before(async () => {
|
||||
dep1 = await createDepartment(
|
||||
{
|
||||
enabled: true,
|
||||
name: Random.id(),
|
||||
showOnRegistration: true,
|
||||
email: `${Random.id()}@example.com`,
|
||||
showOnOfflineForm: true,
|
||||
},
|
||||
[{ agentId: credentials['X-User-Id'] }],
|
||||
);
|
||||
dep2 = await createDepartment(
|
||||
{
|
||||
enabled: false,
|
||||
name: Random.id(),
|
||||
email: `${Random.id()}@example.com`,
|
||||
showOnRegistration: true,
|
||||
showOnOfflineForm: true,
|
||||
},
|
||||
[{ agentId: credentials['X-User-Id'] }],
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteDepartment(dep1._id);
|
||||
await deleteDepartment(dep2._id);
|
||||
});
|
||||
describe('with no permission', () => {
|
||||
before(async () => {
|
||||
await updatePermission('view-l-room', []);
|
||||
});
|
||||
after(async () => {
|
||||
await updatePermission('view-l-room', ['livechat-manager', 'livechat-agent', 'admin']);
|
||||
});
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await request
|
||||
.get(api(`livechat/agents/${agent._id}/departments`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
});
|
||||
it('should return an empty array of departments when the agentId is invalid', async () => {
|
||||
await request
|
||||
.get(api('livechat/agents/invalid-id/departments'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('departments').and.to.be.an('array');
|
||||
});
|
||||
});
|
||||
it('should return an array of departments when the agentId is valid', async () => {
|
||||
await request
|
||||
.get(api(`livechat/agents/${agent._id}/departments`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('departments').and.to.be.an('array');
|
||||
expect(res.body.departments.length).to.be.equal(2);
|
||||
(res.body.departments as ILivechatDepartment[]).forEach((department) => {
|
||||
expect(department.agentId).to.be.equal(agent._id);
|
||||
expect(department).to.have.property('departmentName').that.is.a('string');
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should return only enabled departments when param `enabledDepartmentsOnly` is true ', async () => {
|
||||
await request
|
||||
.get(api(`livechat/agents/${agent._id}/departments`))
|
||||
.set(credentials)
|
||||
.query({ enabledDepartmentsOnly: true })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('departments').and.to.be.an('array');
|
||||
expect(res.body.departments.length).to.be.equal(1);
|
||||
expect(res.body.departments[0].departmentEnabled).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/agent.info/:rid/:token', () => {
|
||||
let room: IOmnichannelRoom;
|
||||
let visitor: ILivechatVisitor;
|
||||
before(async () => {
|
||||
visitor = await createVisitor();
|
||||
room = await createLivechatRoom(visitor.token);
|
||||
});
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
it('should fail when token in url params is not valid', async () => {
|
||||
await request.get(api(`livechat/agent.info/soemthing/invalid-token`)).expect(400);
|
||||
});
|
||||
it('should fail when token is valid but rid isnt', async () => {
|
||||
const visitor = await createVisitor();
|
||||
await request.get(api(`livechat/agent.info/invalid-rid/${visitor.token}`)).expect(400);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
/* it('should fail when room is not being served by any agent', async () => {
|
||||
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await request.get(api(`livechat/agent.info/${room._id}/${visitor.token}`)).expect(400);
|
||||
}); */
|
||||
it('should return a valid agent when the room is being served and the room belongs to visitor', async () => {
|
||||
const inq = await fetchInquiry(room._id);
|
||||
await takeInquiry(inq._id, agent2.credentials);
|
||||
|
||||
const { body } = await request.get(api(`livechat/agent.info/${room._id}/${visitor.token}`));
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('agent');
|
||||
expect(body.agent).to.have.property('_id', agent2.user._id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/agent.next/:token', () => {
|
||||
let visitor: ILivechatVisitor;
|
||||
let room: IOmnichannelRoom;
|
||||
before(async () => {
|
||||
visitor = await createVisitor();
|
||||
room = await createLivechatRoom(visitor.token);
|
||||
});
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
it('should fail when token in url params is not valid', async () => {
|
||||
await request.get(api(`livechat/agent.next/invalid-token`)).expect(400);
|
||||
});
|
||||
it('should return success when visitor with token has an open room', async () => {
|
||||
await request.get(api(`livechat/agent.next/${visitor.token}`)).expect(200);
|
||||
});
|
||||
|
||||
// TODO: test cases when algo is Auto_Selection
|
||||
});
|
||||
|
||||
describe('livechat/agent.next/:token - with manual selection', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
|
||||
});
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
|
||||
});
|
||||
it('should fail if theres no open room for visitor and algo is manual selection', async () => {
|
||||
const visitor = await createVisitor();
|
||||
|
||||
await request.get(api(`livechat/agent.next/${visitor.token}`)).expect(400);
|
||||
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/agent.status', () => {
|
||||
describe('with no view-l permission', () => {
|
||||
before(async () => {
|
||||
await removePermissionFromAllRoles('view-l-room');
|
||||
});
|
||||
after(async () => {
|
||||
await restorePermissionToRoles('view-l-room');
|
||||
});
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission to change its own status', async () => {
|
||||
await request.post(api('livechat/agent.status')).set(credentials).send({ status: 'not-available' }).expect(403);
|
||||
});
|
||||
});
|
||||
describe('with no manage-agent permission', () => {
|
||||
before(async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(credentials)
|
||||
.send({ status: 'not-available', agentId: agent2.user._id })
|
||||
.expect(200);
|
||||
await removePermissionFromAllRoles('manage-livechat-agents');
|
||||
});
|
||||
after(async () => {
|
||||
await restorePermissionToRoles('manage-livechat-agents');
|
||||
});
|
||||
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission to change other status', async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(credentials)
|
||||
.send({ status: 'available', agentId: agent2.user._id })
|
||||
.expect(403);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an error if user is not an agent', async () => {
|
||||
const user = await createUser({ roles: ['livechat-manager'] });
|
||||
const userCredentials = await login(user.username, password);
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(userCredentials)
|
||||
.send({ status: 'available', agentId: user._id })
|
||||
.expect(404)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'Agent not found');
|
||||
});
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
it('should return an error if status is not valid', async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(agent2.credentials)
|
||||
.send({ status: 'invalid-status', agentId: agent2.user._id })
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error');
|
||||
});
|
||||
});
|
||||
it('should return an error if agentId param is not valid', async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(agent2.credentials)
|
||||
.send({ status: 'available', agentId: 'invalid-agent-id' })
|
||||
.expect(404)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'Agent not found');
|
||||
});
|
||||
});
|
||||
|
||||
describe('cases for valid agents', () => {
|
||||
let currentUser: ILivechatAgent;
|
||||
before(async () => {
|
||||
currentUser = await getMe(agent2.credentials);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(agent2.credentials)
|
||||
.send({ status: 'not-available', agentId: currentUser._id })
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
it('should be able to set a logged in users status to available', async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(agent2.credentials)
|
||||
.send({ status: 'available', agentId: currentUser._id })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('status', 'available');
|
||||
});
|
||||
});
|
||||
it('should allow managers to set other agents status to available', async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(credentials)
|
||||
.send({ status: 'available', agentId: currentUser._id })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('status', 'available');
|
||||
});
|
||||
});
|
||||
describe('outside of business hours', () => {
|
||||
before(async () => {
|
||||
await makeDefaultBusinessHourActiveAndClosed();
|
||||
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(agent2.credentials)
|
||||
.send({ status: 'not-available', agentId: currentUser._id })
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await disableDefaultBusinessHour();
|
||||
});
|
||||
it('should throw an error if agent tries to make themselves available outside of Business hour', async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(agent2.credentials)
|
||||
.send({ status: 'available', agentId: currentUser._id })
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'error-business-hours-are-closed');
|
||||
});
|
||||
});
|
||||
it('should return success but not change the agent when admin make try other agents available outside business hour', async () => {
|
||||
await request
|
||||
.post(api('livechat/agent.status'))
|
||||
.set(credentials)
|
||||
.send({ status: 'available', agentId: currentUser._id })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('status', 'not-available');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Agent sidebar', () => {
|
||||
let testUser: { user: IUser; credentials: Credentials };
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentAvailable(credentials2);
|
||||
|
||||
testUser = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
after(async () => {
|
||||
await deleteUser(testUser.user);
|
||||
});
|
||||
|
||||
it('should return an empty list of rooms for a newly created agent', async () => {
|
||||
const { body } = await request.get(api('rooms.get')).set(testUser.credentials).send({}).expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.update.filter((r: IRoom) => r.t === 'l')).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
describe('with a room', () => {
|
||||
let room: IOmnichannelRoom;
|
||||
let visitor: ILivechatVisitor;
|
||||
|
||||
before(async () => {
|
||||
const { room: r, visitor: v } = await startANewLivechatRoomAndTakeIt({ agent: testUser.credentials });
|
||||
room = r;
|
||||
visitor = v;
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
|
||||
it('should have a new room in his sidebar after taking a conversation from the queue', async () => {
|
||||
const { body } = await request.get(api('rooms.get')).set(testUser.credentials).send({}).expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
const livechatRooms = body.update.filter((r: IRoom) => r.t === 'l');
|
||||
expect(livechatRooms).to.have.lengthOf(1);
|
||||
expect(body.update.find((r: { _id: string }) => r._id === room._id)).to.be.an('object');
|
||||
expect(body.update.find((r: { _id: string }) => r._id === 'GENERAL')).to.be.an('object');
|
||||
});
|
||||
|
||||
it('should not have the room if user moves room back to queue', async () => {
|
||||
await moveBackToQueue(room._id, testUser.credentials);
|
||||
|
||||
const { body } = await request
|
||||
.get(api('rooms.get'))
|
||||
.set(testUser.credentials)
|
||||
.query({ updatedSince: new Date(new Date().getTime() - 2000) })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.update.find((r: { _id: string }) => r._id === room._id)).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have the room if the user closes the room', async () => {
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt({ agent: testUser.credentials });
|
||||
|
||||
await closeOmnichannelRoom(room._id);
|
||||
|
||||
const { body } = await request.get(api('rooms.get')).set(testUser.credentials).expect(200);
|
||||
|
||||
expect(body.update.find((r: { _id: string }) => r._id === room._id)).to.be.undefined;
|
||||
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
});
|
||||
});
|
||||
265
apps/meteor/tests/end-to-end/api/livechat/02-appearance.ts
Normal file
265
apps/meteor/tests/end-to-end/api/livechat/02-appearance.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
import { expect } from 'chai';
|
||||
import { before, describe, it } from 'mocha';
|
||||
import type { Response } from 'supertest';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { sleep } from '../../../data/livechat/utils';
|
||||
import { removePermissionFromAllRoles, restorePermissionToRoles, updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
describe('LIVECHAT - appearance', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
});
|
||||
|
||||
describe('livechat/appearance', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request.get(api('livechat/appearance')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
});
|
||||
it('should return an array of settings', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
await request
|
||||
.get(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.appearance).to.be.an('array');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST livechat/appearance', () => {
|
||||
it('should fail if user is not logged in', async () => {
|
||||
await request.post(api('livechat/appearance')).send({}).expect(401);
|
||||
});
|
||||
it('should fail if body is not an array', async () => {
|
||||
await request.post(api('livechat/appearance')).set(credentials).send({}).expect(400);
|
||||
});
|
||||
it('should fail if body is an empty array', async () => {
|
||||
await request.post(api('livechat/appearance')).set(credentials).send([]).expect(400);
|
||||
});
|
||||
it('should fail if body does not contain value', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ name: 'Livechat_title' }])
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if body does not contain name', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ value: 'test' }])
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if user does not have the necessary permission', async () => {
|
||||
await removePermissionFromAllRoles('view-livechat-manager');
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'invalid', value: 'test' }])
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if body contains invalid _id', async () => {
|
||||
await restorePermissionToRoles('view-livechat-manager');
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'invalid', value: 'test' }])
|
||||
.expect(400);
|
||||
});
|
||||
it('should update the settings', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_title', value: 'test' }])
|
||||
.expect(200);
|
||||
});
|
||||
// Test for: https://github.com/ajv-validator/ajv/issues/1140
|
||||
it('should update a boolean setting and keep it as boolean', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_registration_form', value: true }])
|
||||
.expect(200);
|
||||
|
||||
// Just enough to get the stream to update cached settings
|
||||
await sleep(500);
|
||||
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.registrationForm).to.be.true;
|
||||
});
|
||||
it('should update a boolean setting and keep it as boolean', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_registration_form', value: false }])
|
||||
.expect(200);
|
||||
|
||||
// Just enough to get the stream to update cached settings
|
||||
await sleep(500);
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.registrationForm).to.be.false;
|
||||
});
|
||||
it('should update a number setting and keep it as number', async () => {
|
||||
await updateSetting('Livechat_enable_message_character_limit', true);
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_message_character_limit', value: 100 }])
|
||||
.expect(200);
|
||||
|
||||
// Just enough to get the stream to update cached settings
|
||||
await sleep(500);
|
||||
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.limitTextLength).to.be.equal(100);
|
||||
await updateSetting('Livechat_enable_message_character_limit', false);
|
||||
});
|
||||
it('should coerce the value of a setting based on its stored datatype (int)', async () => {
|
||||
await updateSetting('Livechat_enable_message_character_limit', true);
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_message_character_limit', value: '100' }])
|
||||
.expect(200);
|
||||
// Just enough to get the stream to update cached settings
|
||||
await sleep(500);
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.limitTextLength).to.be.equal(100);
|
||||
await updateSetting('Livechat_enable_message_character_limit', false);
|
||||
});
|
||||
it('should coerce the value of a setting based on its stored datatype (boolean)', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_registration_form', value: 'true' }])
|
||||
.expect(200);
|
||||
|
||||
// Just enough to get the stream to update cached settings
|
||||
await sleep(500);
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.registrationForm).to.be.true;
|
||||
});
|
||||
it('should coerce an invalid number value to zero', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([
|
||||
{ _id: 'Livechat_message_character_limit', value: 'xxxx' },
|
||||
{ _id: 'Livechat_enable_message_character_limit', value: true },
|
||||
])
|
||||
.expect(200);
|
||||
|
||||
// Just enough to get the stream to update cached settings
|
||||
await sleep(500);
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
// When setting is 0, we default to Message_MaxAllowedSize value
|
||||
expect(body.config.settings.limitTextLength).to.be.equal(5000);
|
||||
});
|
||||
it('should coerce a boolean value on an int setting to 0', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([
|
||||
{ _id: 'Livechat_message_character_limit', value: true },
|
||||
{ _id: 'Livechat_enable_message_character_limit', value: true },
|
||||
])
|
||||
.expect(200);
|
||||
|
||||
// Just enough to get the stream to update cached settings
|
||||
await sleep(500);
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.limitTextLength).to.be.equal(5000);
|
||||
});
|
||||
it('should coerce a non boolean value on a boolean setting to false', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_enable_message_character_limit', value: 'xxxx' }])
|
||||
.expect(200);
|
||||
|
||||
// Just enough to get the stream to update cached settings
|
||||
await sleep(500);
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.limitTextLength).to.be.false;
|
||||
});
|
||||
|
||||
(IS_EE ? it : it.skip)('should accept an array setting', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_hide_system_messages', value: ['livechat-started'] }])
|
||||
.expect(200);
|
||||
await sleep(500);
|
||||
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.hiddenSystemMessages).to.be.an('array');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.include('livechat-started');
|
||||
});
|
||||
|
||||
(IS_EE ? it : it.skip)('should accept an array setting with multiple values', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_hide_system_messages', value: ['uj', 'livechat_transfer_history'] }])
|
||||
.expect(200);
|
||||
await sleep(500);
|
||||
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.hiddenSystemMessages).to.be.an('array');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.include('uj');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.include('livechat_transfer_history');
|
||||
expect(body.config.settings.hiddenSystemMessages).not.to.include('livechat-started');
|
||||
});
|
||||
|
||||
(IS_EE ? it : it.skip)('should not update an array setting with a value other than array', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_hide_system_messages', value: 'uj' }])
|
||||
.expect(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.hiddenSystemMessages).to.be.an('array');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.include('uj');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.include('livechat_transfer_history');
|
||||
});
|
||||
|
||||
(IS_EE ? it : it.skip)('should not update an array setting with values that are not valid setting values', async () => {
|
||||
await request
|
||||
.post(api('livechat/appearance'))
|
||||
.set(credentials)
|
||||
.send([{ _id: 'Livechat_hide_system_messages', value: ['livechat-started', 'invalid'] }])
|
||||
.expect(200);
|
||||
|
||||
await sleep(500);
|
||||
|
||||
// Get data from livechat/config
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials).expect(200);
|
||||
expect(body.config.settings.hiddenSystemMessages).to.be.an('array');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.include('uj');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.include('livechat_transfer_history');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.not.include('livechat-started');
|
||||
expect(body.config.settings.hiddenSystemMessages).to.not.include('invalid');
|
||||
});
|
||||
});
|
||||
});
|
||||
746
apps/meteor/tests/end-to-end/api/livechat/03-custom-fields.ts
Normal file
746
apps/meteor/tests/end-to-end/api/livechat/03-custom-fields.ts
Normal file
@@ -0,0 +1,746 @@
|
||||
import type { ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { after, before, describe, it } from 'mocha';
|
||||
import type { Response } from 'supertest';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { createCustomField, deleteCustomField } from '../../../data/livechat/custom-fields';
|
||||
import { closeOmnichannelRoom, createLivechatRoom, createVisitor, deleteVisitor, getLivechatRoomInfo } from '../../../data/livechat/rooms';
|
||||
import { updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
|
||||
describe('LIVECHAT - custom fields', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
});
|
||||
|
||||
describe('livechat/custom-fields', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-l-room', []);
|
||||
await request.get(api('livechat/custom-fields')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
});
|
||||
it('should return an array of custom fields', async () => {
|
||||
await updatePermission('view-l-room', ['admin']);
|
||||
await request
|
||||
.get(api('livechat/custom-fields'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.customFields).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
});
|
||||
});
|
||||
it('should return an array of custom fields even requested with count and offset params', async () => {
|
||||
await updatePermission('view-l-room', ['admin']);
|
||||
await request
|
||||
.get(api('livechat/custom-fields'))
|
||||
.set(credentials)
|
||||
.query({
|
||||
count: 5,
|
||||
offset: 0,
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.customFields).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/custom-fields/id', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-l-room', []);
|
||||
await request.get(api('livechat/custom-fields/invalid-id')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
await updatePermission('view-l-room', ['admin']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/custom.field', () => {
|
||||
it('should fail when token is not on body params', async () => {
|
||||
await request.post(api('livechat/custom.field')).expect(400);
|
||||
});
|
||||
it('should fail when key is not on body params', async () => {
|
||||
await request.post(api('livechat/custom.field')).send({ token: 'invalid-token' }).expect(400);
|
||||
});
|
||||
it('should fail when value is not on body params', async () => {
|
||||
await request.post(api('livechat/custom.field')).send({ token: 'invalid-token', key: 'invalid-key' }).expect(400);
|
||||
});
|
||||
it('should fail when overwrite is not on body params', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: 'invalid-token', key: 'invalid-key', value: 'invalid-value' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail when token is invalid', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: 'invalid-token', key: 'invalid-key', value: 'invalid-value', overwrite: true })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail when key is invalid', async () => {
|
||||
const visitor = await createVisitor();
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: visitor.token, key: 'invalid-key', value: 'invalid-value', overwrite: true })
|
||||
.expect(400);
|
||||
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
it('should save a custom field on visitor', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const customFieldName = `new_custom_field_${Date.now()}`;
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: customFieldName,
|
||||
label: customFieldName,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: visitor.token, key: customFieldName, value: 'test_address', overwrite: true })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('field');
|
||||
expect(body.field).to.have.property('value', 'test_address');
|
||||
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/custom.fields', () => {
|
||||
const customFieldName = `new_custom_field_${Date.now()}_1`;
|
||||
const customFieldName2 = `new_custom_field_${Date.now()}_2`;
|
||||
const customFieldName3 = `new_custom_field_${Date.now()}_3`;
|
||||
const roomCustomField = `new_custom_field_${Date.now()}_4`;
|
||||
let visitor: ILivechatVisitor;
|
||||
let visitorRoom: IOmnichannelRoom;
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: customFieldName,
|
||||
label: customFieldName,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: customFieldName2,
|
||||
label: customFieldName2,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: customFieldName3,
|
||||
label: customFieldName3,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: roomCustomField,
|
||||
label: roomCustomField,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'room',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
visitor = await createVisitor();
|
||||
// start a room for visitor2
|
||||
visitorRoom = await createLivechatRoom(visitor.token);
|
||||
});
|
||||
after(async () => {
|
||||
await Promise.all([
|
||||
deleteCustomField(customFieldName),
|
||||
deleteCustomField(customFieldName2),
|
||||
deleteCustomField(customFieldName3),
|
||||
deleteCustomField(roomCustomField),
|
||||
closeOmnichannelRoom(visitorRoom._id),
|
||||
deleteVisitor(visitor.token),
|
||||
updateSetting('Livechat_accept_chats_with_no_agents', false),
|
||||
]);
|
||||
});
|
||||
it('should fail when token is not on body params', async () => {
|
||||
await request.post(api('livechat/custom.fields')).expect(400);
|
||||
});
|
||||
it('should fail if customFields is not on body params', async () => {
|
||||
await request.post(api('livechat/custom.fields')).send({ token: 'invalid-token' }).expect(400);
|
||||
});
|
||||
it('should fail if customFields is not an array', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: 'invalid-token',
|
||||
customFields: 'invalid-custom-fields',
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if customFields is an empty array', async () => {
|
||||
await request.post(api('livechat/custom.fields')).send({ token: 'invalid-token', customFields: [] }).expect(400);
|
||||
});
|
||||
it('should fail if customFields is an array with invalid objects', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({ token: 'invalid-token', customFields: [{}] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if token is not a valid token', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: 'invalid-token',
|
||||
customFields: [{ key: 'invalid-key', value: 'invalid-value', overwrite: true }],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail when customFields.key is invalid', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [{ key: 'invalid-key', value: 'invalid-value', overwrite: true }],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should save a custom field on visitor', async () => {
|
||||
const visitor = await createVisitor();
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [{ key: customFieldName, value: 'test_address', overwrite: true }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('fields');
|
||||
expect(body.fields).to.be.an('array');
|
||||
expect(body.fields).to.have.lengthOf(1);
|
||||
expect(body.fields[0]).to.have.property('value', 'test_address');
|
||||
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
it('should save multiple custom fields on a visitor', async () => {
|
||||
const visitor = await createVisitor();
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [
|
||||
{ key: customFieldName, value: 'test_address', overwrite: true },
|
||||
{ key: customFieldName2, value: 'test_address2', overwrite: true },
|
||||
{ key: customFieldName3, value: 'test_address3', overwrite: true },
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('fields');
|
||||
expect(body.fields).to.be.an('array');
|
||||
expect(body.fields).to.have.lengthOf(3);
|
||||
expect(body.fields[0]).to.have.property('value', 'test_address');
|
||||
expect(body.fields[1]).to.have.property('value', 'test_address2');
|
||||
expect(body.fields[2]).to.have.property('value', 'test_address3');
|
||||
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
it('should save multiple custom fields on contact when visitor already has custom fields and an update with multiple fields is issued', async () => {
|
||||
const { body } = await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [{ key: customFieldName, value: 'test_address', overwrite: true }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('fields');
|
||||
expect(body.fields).to.be.an('array');
|
||||
expect(body.fields).to.have.lengthOf(1);
|
||||
expect(body.fields[0]).to.have.property('value', 'test_address');
|
||||
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [
|
||||
{ key: customFieldName2, value: 'test_address2', overwrite: true },
|
||||
{ key: customFieldName3, value: 'test_address3', overwrite: true },
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId: visitorRoom.contactId })
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName, 'test_address');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName2, 'test_address2');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName3, 'test_address3');
|
||||
});
|
||||
});
|
||||
it('should mark a conflict on a contact custom fields when overwrite is true and visitor already has the custom field set', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [{ key: customFieldName, value: 'test_address_conflict', overwrite: false }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId: visitorRoom.contactId })
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName, 'test_address');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName2, 'test_address2');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName3, 'test_address3');
|
||||
expect(res.body.contact).to.have.property('conflictingFields').that.is.an('array');
|
||||
expect(res.body.contact.conflictingFields[0]).to.deep.equal({
|
||||
field: `customFields.${customFieldName}`,
|
||||
value: 'test_address_conflict',
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should overwrite the contact custom field when overwrite is true', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [{ key: customFieldName2, value: 'test_new_add', overwrite: true }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId: visitorRoom.contactId })
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName, 'test_address');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName2, 'test_new_add');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName3, 'test_address3');
|
||||
expect(res.body.contact).to.have.property('conflictingFields').that.is.an('array');
|
||||
expect(res.body.contact.conflictingFields[0]).to.deep.equal({
|
||||
field: `customFields.${customFieldName}`,
|
||||
value: 'test_address_conflict',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a room', () => {
|
||||
let visitor: ILivechatVisitor;
|
||||
let room: IOmnichannelRoom;
|
||||
before(async () => {
|
||||
visitor = await createVisitor();
|
||||
room = await createLivechatRoom(visitor.token);
|
||||
});
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
|
||||
it('should save both room & vistor custom fields on one call', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [
|
||||
{ key: customFieldName, value: 'test_address', overwrite: true },
|
||||
{ key: roomCustomField, value: 'test_address2', overwrite: true },
|
||||
{ key: customFieldName3, value: 'test_address3', overwrite: true },
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId: visitorRoom.contactId })
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName, 'test_address');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName3, 'test_address3');
|
||||
});
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
expect(roomInfo).to.have.property('livechatData').that.is.an('object');
|
||||
expect(roomInfo.livechatData).to.have.property(roomCustomField, 'test_address2');
|
||||
});
|
||||
it('should ignore a room custom field when room already has a value for it and overwrite is false', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.fields'))
|
||||
.send({
|
||||
token: visitor.token,
|
||||
customFields: [{ key: roomCustomField, value: 'test_value_overriden', overwrite: false }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
expect(roomInfo).to.have.property('livechatData').that.is.an('object');
|
||||
expect(roomInfo.livechatData).to.have.property(roomCustomField, 'test_address2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/custom.field [with Contacts]', () => {
|
||||
let visitor: ILivechatVisitor;
|
||||
let contactId: string;
|
||||
let room: IOmnichannelRoom;
|
||||
|
||||
const customFieldName = `custom_field_${Date.now()}`;
|
||||
const customFieldValue = 'custom-field-value';
|
||||
|
||||
before(async () => {
|
||||
await updatePermission('create-livechat-contact', ['admin']);
|
||||
await updatePermission('view-livechat-contact', ['admin']);
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
|
||||
// Create a Visitor
|
||||
visitor = await createVisitor();
|
||||
|
||||
// Create a Contact and store id on var
|
||||
await request
|
||||
.post(api('omnichannel/contacts'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: visitor.name,
|
||||
emails: [visitor.visitorEmails?.[0].address],
|
||||
phones: [visitor.phone?.[0].phoneNumber],
|
||||
})
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('contactId');
|
||||
|
||||
contactId = res.body.contactId;
|
||||
});
|
||||
|
||||
room = await createLivechatRoom(visitor.token);
|
||||
|
||||
// Create Custom Field
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: customFieldName,
|
||||
label: customFieldName,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: `${customFieldName}_2`,
|
||||
label: `${customFieldName}_2`,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
// TODO: add clean up for contacts, visitors, etc
|
||||
await Promise.all([
|
||||
deleteCustomField(customFieldName),
|
||||
deleteCustomField(`${customFieldName}_2`),
|
||||
deleteCustomField(`${customFieldName}_3`),
|
||||
deleteVisitor(visitor.token),
|
||||
closeOmnichannelRoom(room._id),
|
||||
updateSetting('Livechat_accept_chats_with_no_agents', false),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should save the custom field on Contact when available', async () => {
|
||||
// Save the custom field on Visitor/Contact
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: visitor.token, key: customFieldName, value: customFieldValue, overwrite: true })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
// Fetch the visitor to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`livechat/visitor/${visitor.token}`))
|
||||
.set(credentials)
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.visitor).to.have.property('livechatData');
|
||||
expect(res.body.visitor.livechatData).to.have.property(customFieldName, customFieldValue);
|
||||
});
|
||||
|
||||
// Fetch the visitor's contact to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName, customFieldValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('Should save multiple custom fields on Contact when available', async () => {
|
||||
// Save the custom field on Visitor/Contact
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: visitor.token, key: customFieldName, value: customFieldValue, overwrite: true })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: visitor.token, key: `${customFieldName}_2`, value: customFieldValue, overwrite: true })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
// Fetch the visitor to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`livechat/visitor/${visitor.token}`))
|
||||
.set(credentials)
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.visitor).to.have.property('livechatData');
|
||||
expect(res.body.visitor.livechatData).to.have.property(customFieldName, customFieldValue);
|
||||
expect(res.body.visitor.livechatData).to.have.property(`${customFieldName}_2`, customFieldValue);
|
||||
});
|
||||
|
||||
// Fetch the visitor's contact to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName, customFieldValue);
|
||||
expect(res.body.contact.customFields).to.have.property(`${customFieldName}_2`, customFieldValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add the custom field as conflict on Contact when overwrite is false', async () => {
|
||||
const conflictingFieldValue = 'conflicting-custom-field-value';
|
||||
|
||||
// Save the custom field on Contact
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: visitor.token, key: customFieldName, value: conflictingFieldValue, overwrite: false })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
// Fetch the visitor to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`livechat/visitor/${visitor.token}`))
|
||||
.set(credentials)
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.visitor).to.have.property('livechatData');
|
||||
expect(res.body.visitor.livechatData).to.have.property(customFieldName, customFieldValue);
|
||||
});
|
||||
|
||||
// Fetch the visitor's contact to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(customFieldName, customFieldValue);
|
||||
|
||||
// Validate custom fields contain both entries, indicating conflict criteria
|
||||
expect(res.body.contact.conflictingFields).to.have.lengthOf(1);
|
||||
expect(res.body.contact.conflictingFields[0]).to.have.property('field', `customFields.${customFieldName}`);
|
||||
expect(res.body.contact.conflictingFields[0]).to.have.property('value', conflictingFieldValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not save the custom field as a conflict on Contact when overwrite is false but custom field is not registered yet', async () => {
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: `${customFieldName}_3`,
|
||||
label: `${customFieldName}_3`,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
|
||||
// Save the custom field on Contact
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: visitor.token, key: `${customFieldName}_3`, value: customFieldValue, overwrite: false })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
// Fetch the visitor to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`livechat/visitor/${visitor.token}`))
|
||||
.set(credentials)
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.visitor).to.have.property('livechatData');
|
||||
expect(res.body.visitor.livechatData).to.have.property(`${customFieldName}_3`, customFieldValue);
|
||||
});
|
||||
|
||||
// Fetch the visitor's contact to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(`${customFieldName}_3`, customFieldValue);
|
||||
|
||||
// Validate custom fields contain both entries, indicating conflict criteria
|
||||
expect(res.body.contact.conflictingFields).to.have.lengthOf(1);
|
||||
expect(res.body.contact.conflictingFields[0]).to.not.have.property('field', `customFields.${customFieldName}_3`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('conflictingField not nullish', () => {
|
||||
let room: IOmnichannelRoom;
|
||||
let visitor: ILivechatVisitor;
|
||||
let contactId2: string | undefined;
|
||||
|
||||
before(async () => {
|
||||
visitor = await createVisitor();
|
||||
await request
|
||||
.post(api('omnichannel/contacts'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: visitor.name,
|
||||
emails: [visitor.visitorEmails?.[0].address],
|
||||
phones: [visitor.phone?.[0].phoneNumber],
|
||||
})
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('contactId');
|
||||
|
||||
contactId2 = res.body.contactId;
|
||||
});
|
||||
room = await createLivechatRoom(visitor.token);
|
||||
});
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
|
||||
it('should not save contact conflictingFields as nullish if not modified', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom.field'))
|
||||
.send({ token: visitor.token, key: `${customFieldName}`, value: customFieldValue, overwrite: true })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
// Fetch the visitor's contact to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId: contactId2 })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(`${customFieldName}`, customFieldValue);
|
||||
|
||||
// Validate conflictingFields was not saved as null
|
||||
expect(res.body.contact).to.not.have.property('conflictingFields');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not save contact conflictingFields as nullish if not modified (through visitor update)', async () => {
|
||||
await request
|
||||
.post(api('livechat/visitor'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
visitor: {
|
||||
token: visitor.token,
|
||||
customFields: [{ key: `${customFieldName}`, value: customFieldValue, overwrite: true }],
|
||||
},
|
||||
});
|
||||
|
||||
// Fetch the visitor's contact to validate custom fields are properly set.
|
||||
await request
|
||||
.get(api(`omnichannel/contacts.get`))
|
||||
.set(credentials)
|
||||
.query({ contactId: contactId2 })
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('contact');
|
||||
expect(res.body.contact).to.have.property('customFields');
|
||||
expect(res.body.contact.customFields).to.have.property(`${customFieldName}`, customFieldValue);
|
||||
|
||||
// Validate conflictingFields was not saved as null
|
||||
expect(res.body.contact).to.not.have.property('conflictingFields');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
1473
apps/meteor/tests/end-to-end/api/livechat/04-dashboards.ts
Normal file
1473
apps/meteor/tests/end-to-end/api/livechat/04-dashboards.ts
Normal file
File diff suppressed because it is too large
Load Diff
611
apps/meteor/tests/end-to-end/api/livechat/05-inquiries.ts
Normal file
611
apps/meteor/tests/end-to-end/api/livechat/05-inquiries.ts
Normal file
@@ -0,0 +1,611 @@
|
||||
import type { Credentials } from '@rocket.chat/api-client';
|
||||
import type { ILivechatDepartment, ILivechatInquiryRecord, ILivechatVisitor, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { before, describe, it, after } from 'mocha';
|
||||
import type { Response } from 'supertest';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { deleteDepartment } from '../../../data/livechat/department';
|
||||
import {
|
||||
closeOmnichannelRoom,
|
||||
createAgent,
|
||||
createDepartment,
|
||||
createLivechatRoom,
|
||||
createVisitor,
|
||||
deleteVisitor,
|
||||
fetchInquiry,
|
||||
getLivechatRoomInfo,
|
||||
makeAgentAvailable,
|
||||
startANewLivechatRoomAndTakeIt,
|
||||
takeInquiry,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import {
|
||||
removePermissionFromAllRoles,
|
||||
restorePermissionToRoles,
|
||||
updateEESetting,
|
||||
updatePermission,
|
||||
updateSetting,
|
||||
} from '../../../data/permissions.helper';
|
||||
import { password } from '../../../data/user';
|
||||
import { createUser, login, deleteUser } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
import { retry } from '../helpers/retry';
|
||||
|
||||
describe('LIVECHAT - inquiries', () => {
|
||||
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');
|
||||
});
|
||||
|
||||
describe('livechat/inquiries.list', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request.get(api('livechat/inquiries.list')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
});
|
||||
it('should return an array of inquiries', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
await request
|
||||
.get(api('livechat/inquiries.list'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.inquiries).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/inquiries.getOne', () => {
|
||||
let room: IOmnichannelRoom;
|
||||
let visitor: ILivechatVisitor;
|
||||
before(async () => {
|
||||
await createAgent();
|
||||
await makeAgentAvailable();
|
||||
|
||||
visitor = await createVisitor();
|
||||
|
||||
room = await createLivechatRoom(visitor.token);
|
||||
});
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-l-room', []);
|
||||
await request
|
||||
.get(api('livechat/inquiries.getOne'))
|
||||
.query({ roomId: 'room-id' })
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should return a inquiry', async () => {
|
||||
await updatePermission('view-l-room', ['admin']);
|
||||
await request
|
||||
.get(api('livechat/inquiries.getOne'))
|
||||
.query({ roomId: 'room-id' })
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('inquiry');
|
||||
});
|
||||
});
|
||||
|
||||
it('should get an inquiry by room id', async () => {
|
||||
const inquiry = await fetchInquiry(room._id);
|
||||
await request
|
||||
.get(api(`livechat/inquiries.getOne`))
|
||||
.query({ roomId: room._id })
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('inquiry');
|
||||
expect(res.body.inquiry).to.have.property('_id', inquiry._id);
|
||||
expect(res.body.inquiry).to.have.property('rid', room._id);
|
||||
expect(res.body.inquiry).to.have.property('ts');
|
||||
expect(res.body.inquiry.ts).to.be.a('string');
|
||||
expect(res.body.inquiry).to.have.property('status', 'queued');
|
||||
expect(res.body.inquiry).to.have.property('name', visitor.name);
|
||||
expect(res.body.inquiry).to.have.property('t', 'l');
|
||||
expect(res.body.inquiry).to.have.property('priorityWeight');
|
||||
expect(res.body.inquiry).to.have.property('estimatedWaitingTimeQueue');
|
||||
expect(res.body.inquiry.source).to.have.property('type', 'api');
|
||||
expect(res.body.inquiry).to.have.property('_updatedAt');
|
||||
expect(res.body.inquiry).to.have.property('v').and.be.an('object');
|
||||
expect(res.body.inquiry.v).to.have.property('_id', visitor._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST livechat/inquiries.take', () => {
|
||||
let takenRoom: IOmnichannelRoom;
|
||||
let servedByRoom: IOmnichannelRoom;
|
||||
let visitor: ILivechatVisitor;
|
||||
let visitor2: ILivechatVisitor;
|
||||
before(async () => {
|
||||
visitor = await createVisitor();
|
||||
visitor2 = await createVisitor();
|
||||
|
||||
takenRoom = await createLivechatRoom(visitor.token);
|
||||
servedByRoom = await createLivechatRoom(visitor2.token);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(takenRoom._id);
|
||||
await closeOmnichannelRoom(servedByRoom._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
await deleteVisitor(visitor2.token);
|
||||
});
|
||||
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-l-room', []);
|
||||
await request
|
||||
.post(api('livechat/inquiries.take'))
|
||||
.set(credentials)
|
||||
.send({ inquiryId: 'room-id' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should throw an error when userId is provided but is invalid', async () => {
|
||||
await updatePermission('view-l-room', ['admin', 'livechat-agent']);
|
||||
await request
|
||||
.post(api('livechat/inquiries.take'))
|
||||
.set(credentials)
|
||||
.send({ inquiryId: 'room-id', userId: 'invalid-user-id' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
});
|
||||
});
|
||||
it('should throw an error if inquiryId is not an string', async () => {
|
||||
await updatePermission('view-l-room', ['admin', 'livechat-agent']);
|
||||
await request
|
||||
.post(api('livechat/inquiries.take'))
|
||||
.set(credentials)
|
||||
.send({ inquiryId: { regexxxx: 'bla' }, userId: 'user-id' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
});
|
||||
});
|
||||
it('should take an inquiry if all params are good', async () => {
|
||||
await updatePermission('view-l-room', ['admin', 'livechat-agent']);
|
||||
const agent = await createAgent();
|
||||
await makeAgentAvailable();
|
||||
const inquiry = await fetchInquiry(takenRoom._id);
|
||||
|
||||
await request
|
||||
.post(api('livechat/inquiries.take'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
inquiryId: inquiry._id,
|
||||
userId: agent._id,
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
const inquiry2 = (await fetchInquiry(takenRoom._id)) as ILivechatInquiryRecord;
|
||||
expect(inquiry2.source?.type).to.equal('api');
|
||||
expect(inquiry2.status).to.equal('taken');
|
||||
});
|
||||
it('should mark a taken room as servedBy me', async () => {
|
||||
const agent = await createAgent();
|
||||
await makeAgentAvailable();
|
||||
const inquiry = await fetchInquiry(servedByRoom._id);
|
||||
|
||||
await request
|
||||
.post(api('livechat/inquiries.take'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
inquiryId: inquiry._id,
|
||||
userId: agent._id,
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(servedByRoom._id);
|
||||
|
||||
expect(roomInfo).to.have.property('servedBy').that.is.an('object');
|
||||
expect(roomInfo.servedBy).to.have.property('_id', 'rocketchat.internal.admin.test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/inquiries.queuedForUser', () => {
|
||||
let testUser: { user: IUser; credentials: { [key: string]: string } };
|
||||
const departmentsToDelete: ILivechatDepartment[] = [];
|
||||
const visitorsToDelete: ILivechatVisitor[] = [];
|
||||
const roomsToClose: IOmnichannelRoom[] = [];
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
await updateSetting('Omnichannel_enable_department_removal', true);
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentAvailable(credentials2);
|
||||
|
||||
testUser = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
after(async () => {
|
||||
await Promise.allSettled(roomsToClose.map((room) => closeOmnichannelRoom(room._id)));
|
||||
await Promise.allSettled(visitorsToDelete.map((visitor) => deleteVisitor(visitor.token)));
|
||||
await Promise.allSettled(departmentsToDelete.map((dep) => deleteDepartment(dep._id)));
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', false);
|
||||
await updateSetting('Omnichannel_enable_department_removal', false);
|
||||
await deleteUser(testUser.user);
|
||||
});
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-l-room', []);
|
||||
await request.get(api('livechat/inquiries.queuedForUser')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
});
|
||||
it('should return an array of inquiries', async () => {
|
||||
await restorePermissionToRoles('view-l-room');
|
||||
await request
|
||||
.get(api('livechat/inquiries.queuedForUser'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.inquiries).to.be.an('array');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body).to.have.property('count');
|
||||
});
|
||||
});
|
||||
it('should validate all returned inquiries are queued', async () => {
|
||||
await request
|
||||
.get(api('livechat/inquiries.queuedForUser'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect(async (res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.inquiries).to.be.an('array');
|
||||
for (const inquiry of res.body.inquiries) {
|
||||
expect(inquiry).to.have.property('status', 'queued');
|
||||
}
|
||||
});
|
||||
});
|
||||
it('should return only public inquiries for a user with no departments', async () => {
|
||||
const { body } = await request
|
||||
.get(api('livechat/inquiries.queuedForUser'))
|
||||
.set(testUser.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.inquiries).to.be.an('array');
|
||||
for (const inq of body.inquiries) {
|
||||
expect(inq).to.not.have.property('department');
|
||||
expect(inq).to.have.property('status', 'queued');
|
||||
}
|
||||
});
|
||||
(IS_EE ? it : it.skip)('should return inquiries of the same department as the user', async () => {
|
||||
const dep = await createDepartment([{ agentId: testUser.user._id }]);
|
||||
departmentsToDelete.push(dep);
|
||||
const visitor = await createVisitor(dep._id);
|
||||
visitorsToDelete.push(visitor);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
roomsToClose.push(room);
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/inquiries.queuedForUser'))
|
||||
.set(testUser.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.inquiries).to.be.an('array');
|
||||
const depInq = body.inquiries.filter((inq: { department: string }) => inq.department === dep._id);
|
||||
|
||||
expect(depInq.length).to.be.equal(1);
|
||||
});
|
||||
(IS_EE ? it : it.skip)('should not return an inquiry of a department the user is not part of', async () => {
|
||||
const dep = await createDepartment();
|
||||
departmentsToDelete.push(dep);
|
||||
const visitor = await createVisitor(dep._id);
|
||||
visitorsToDelete.push(visitor);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
roomsToClose.push(room);
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/inquiries.queuedForUser'))
|
||||
.set(testUser.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.inquiries).to.be.an('array');
|
||||
const depInq = body.inquiries.filter((inq: { department: string }) => inq.department === dep._id);
|
||||
|
||||
expect(depInq.length).to.be.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/inquiries.returnAsInquiry', () => {
|
||||
let testUser: { user: IUser; credentials: Credentials };
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentAvailable(credentials2);
|
||||
|
||||
testUser = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
after(async () => {
|
||||
await deleteUser(testUser.user);
|
||||
});
|
||||
|
||||
it('should throw an error if user doesnt have view-l-room permission', async () => {
|
||||
await removePermissionFromAllRoles('view-l-room');
|
||||
const { body } = await request
|
||||
.post(api('livechat/inquiries.returnAsInquiry'))
|
||||
.set(credentials)
|
||||
.send({ roomId: 'test' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
|
||||
expect(body).to.have.property('success', false);
|
||||
expect(body.error).to.have.equal('User does not have the permissions required for this action [error-unauthorized]');
|
||||
});
|
||||
it('should fail if provided room doesnt exists', async () => {
|
||||
await restorePermissionToRoles('view-l-room');
|
||||
const { body } = await request
|
||||
.post(api('livechat/inquiries.returnAsInquiry'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: 'test',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
|
||||
expect(body).to.have.property('success', false);
|
||||
expect(body).to.have.property('error', 'error-room-not-found');
|
||||
});
|
||||
it('should fail if room is not a livechat room', async () => {
|
||||
const { body } = await request
|
||||
.post(api('livechat/inquiries.returnAsInquiry'))
|
||||
.set(credentials)
|
||||
.send({ roomId: 'GENERAL' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
|
||||
expect(body).to.have.property('success', false);
|
||||
expect(body).to.have.property('error', 'error-room-not-found');
|
||||
});
|
||||
it('should fail if room is closed', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/inquiries.returnAsInquiry'))
|
||||
.set(credentials)
|
||||
.send({ roomId: room._id })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
|
||||
expect(body).to.have.property('success', false);
|
||||
expect(body).to.have.property('error', 'room-closed');
|
||||
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
describe('no serving', () => {
|
||||
let room: IOmnichannelRoom;
|
||||
let visitor: ILivechatVisitor;
|
||||
before(async () => {
|
||||
visitor = await createVisitor();
|
||||
room = await createLivechatRoom(visitor.token);
|
||||
});
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
it('should fail if no one is serving the room', async () => {
|
||||
const { body } = await request
|
||||
.post(api('livechat/inquiries.returnAsInquiry'))
|
||||
.set(credentials)
|
||||
.send({ roomId: room._id })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('result', false);
|
||||
});
|
||||
});
|
||||
|
||||
let inquiry: ILivechatInquiryRecord;
|
||||
let room: IOmnichannelRoom;
|
||||
let returnAsInquiryDep: ILivechatDepartment;
|
||||
let returnAsInquiryVisitor: ILivechatVisitor;
|
||||
(IS_EE ? it : it.skip)('should move a room back to queue', async () => {
|
||||
await updateSetting('Omnichannel_enable_department_removal', true);
|
||||
returnAsInquiryDep = await createDepartment([{ agentId: testUser.user._id }]);
|
||||
returnAsInquiryVisitor = await createVisitor(returnAsInquiryDep._id);
|
||||
room = await createLivechatRoom(returnAsInquiryVisitor.token);
|
||||
const inq = await fetchInquiry(room._id);
|
||||
inquiry = inq;
|
||||
await takeInquiry(inq._id, testUser.credentials);
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/inquiries.returnAsInquiry'))
|
||||
.set(testUser.credentials)
|
||||
.send({ roomId: room._id })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('result', true);
|
||||
});
|
||||
(IS_EE ? it : it.skip)('should appear on users queued elements', async () => {
|
||||
const { body } = await request
|
||||
.get(api('livechat/inquiries.queuedForUser'))
|
||||
.set(testUser.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body.inquiries).to.be.an('array');
|
||||
const depInq = body.inquiries.filter((inq: { _id: string }) => inq._id === inquiry._id);
|
||||
|
||||
expect(depInq.length).to.be.equal(1);
|
||||
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(returnAsInquiryVisitor.token);
|
||||
await deleteDepartment(returnAsInquiryDep._id);
|
||||
await updateSetting('Omnichannel_enable_department_removal', false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('keep inquiry last message updated', () => {
|
||||
let room: any;
|
||||
let visitor: any;
|
||||
let agent: any;
|
||||
|
||||
before(async () => {
|
||||
agent = await createAgent();
|
||||
visitor = await createVisitor();
|
||||
|
||||
await makeAgentAvailable();
|
||||
room = await createLivechatRoom(visitor.token);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await deleteVisitor(visitor.token);
|
||||
});
|
||||
|
||||
it('should update inquiry last message', async () => {
|
||||
const msgText = `update inquiry ${Date.now()}`;
|
||||
|
||||
await request.post(api('livechat/message')).send({ token: visitor.token, rid: room._id, msg: msgText }).expect(200);
|
||||
|
||||
const inquiry = await fetchInquiry(room._id);
|
||||
|
||||
expect(inquiry).to.have.property('_id', inquiry._id);
|
||||
expect(inquiry).to.have.property('rid', room._id);
|
||||
expect(inquiry).to.have.property('lastMessage');
|
||||
expect(inquiry.lastMessage).to.have.property('msg', msgText);
|
||||
});
|
||||
|
||||
it('should update room last message after inquiry is taken', async () => {
|
||||
const msgText = `update room ${Date.now()}`;
|
||||
|
||||
const inquiry = await fetchInquiry(room._id);
|
||||
|
||||
await request
|
||||
.post(api('livechat/inquiries.take'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
inquiryId: inquiry._id,
|
||||
userId: agent._id,
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
await request.post(api('livechat/message')).send({ token: visitor.token, rid: room._id, msg: msgText }).expect(200);
|
||||
|
||||
// check room
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
expect(roomInfo).to.have.property('lastMessage');
|
||||
expect(roomInfo.lastMessage).to.have.property('msg', msgText);
|
||||
});
|
||||
|
||||
it('should have the correct last message when room is returned to queue', async () => {
|
||||
const msgText = `return to queue ${Date.now()}`;
|
||||
|
||||
await request.post(api('livechat/message')).send({ token: visitor.token, rid: room._id, msg: msgText }).expect(200);
|
||||
|
||||
await request
|
||||
.post(api('livechat/inquiries.returnAsInquiry'))
|
||||
.set(credentials)
|
||||
.send({ roomId: room._id })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
const inquiry = await fetchInquiry(room._id);
|
||||
|
||||
expect(inquiry).to.have.property('_id', inquiry._id);
|
||||
expect(inquiry).to.have.property('rid', room._id);
|
||||
expect(inquiry).to.have.property('lastMessage');
|
||||
expect(inquiry.lastMessage).to.have.property('msg', msgText);
|
||||
});
|
||||
});
|
||||
|
||||
(IS_EE ? describe : describe.skip)('Auto Transfer Scheduler - Manual_Selection', () => {
|
||||
let testRoom: IOmnichannelRoom;
|
||||
let testDepartment: ILivechatDepartment;
|
||||
let testVisitor: ILivechatVisitor;
|
||||
before(async () => {
|
||||
// seconds
|
||||
await Promise.all([
|
||||
updateSetting('Livechat_auto_transfer_chat_timeout', 3),
|
||||
createAgent(),
|
||||
updateSetting('Omnichannel_enable_department_removal', true),
|
||||
]);
|
||||
testDepartment = await createDepartment([{ agentId: 'rocketchat.internal.admin.test' }]);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteVisitor(testVisitor.token);
|
||||
await deleteDepartment(testDepartment._id);
|
||||
await Promise.all([
|
||||
updateSetting('Livechat_auto_transfer_chat_timeout', 0),
|
||||
updateSetting('Omnichannel_enable_department_removal', false),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should create a room and schedule it for transfer', async () => {
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt({ departmentId: testDepartment._id });
|
||||
testVisitor = visitor;
|
||||
// The room returned is not updated :(
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(updatedRoom).to.have.property('servedBy').that.is.an('object');
|
||||
testRoom = updatedRoom;
|
||||
});
|
||||
it('should return a chat to the queue when not answered after 3 seconds', async () => {
|
||||
await retry(
|
||||
`The agenda has a 'heartbeat' of 3 seconds (for testing environment), so its likely that the first fetch will not have the room updated`,
|
||||
async () => {
|
||||
const inquiry = await fetchInquiry(testRoom._id);
|
||||
const room = await getLivechatRoomInfo(testRoom._id);
|
||||
|
||||
expect(room).to.not.have.property('servedBy');
|
||||
expect(inquiry).to.have.property('status', 'queued');
|
||||
|
||||
await closeOmnichannelRoom(testRoom._id);
|
||||
},
|
||||
{
|
||||
delayMs: 3000,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
300
apps/meteor/tests/end-to-end/api/livechat/06-integrations.ts
Normal file
300
apps/meteor/tests/end-to-end/api/livechat/06-integrations.ts
Normal file
@@ -0,0 +1,300 @@
|
||||
import type { ILivechatVisitor, IOmnichannelRoom, ISetting } 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 {
|
||||
closeOmnichannelRoom,
|
||||
createAgent,
|
||||
createLivechatRoom,
|
||||
createVisitor,
|
||||
deleteVisitor,
|
||||
getLivechatRoomInfo,
|
||||
startANewLivechatRoomAndTakeIt,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import { sleep } from '../../../data/livechat/utils';
|
||||
import { getLivechatVisitorByToken } from '../../../data/livechat/visitor';
|
||||
import { updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
|
||||
describe('LIVECHAT - Integrations', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
});
|
||||
|
||||
describe('livechat/integrations.settings', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request.get(api('livechat/integrations.settings')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
});
|
||||
it('should return an array of settings', (done) => {
|
||||
updatePermission('view-livechat-manager', ['admin'])
|
||||
.then(async () => {
|
||||
const res = await request
|
||||
.get(api('livechat/integrations.settings'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.settings).to.be.an('array');
|
||||
const settingIds = res.body.settings.map((setting: ISetting) => setting._id);
|
||||
expect(settingIds).to.include.members([
|
||||
'Livechat_webhookUrl',
|
||||
'Livechat_secret_token',
|
||||
'Livechat_http_timeout',
|
||||
'Livechat_webhook_on_start',
|
||||
'Livechat_webhook_on_close',
|
||||
'Livechat_webhook_on_chat_taken',
|
||||
'Livechat_webhook_on_chat_queued',
|
||||
'Livechat_webhook_on_forward',
|
||||
'Livechat_webhook_on_offline_msg',
|
||||
'Livechat_webhook_on_visitor_message',
|
||||
'Livechat_webhook_on_agent_message',
|
||||
]);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Incoming SMS', () => {
|
||||
let smsVisitor: ILivechatVisitor;
|
||||
let smsRoom: string;
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('SMS_Enabled', true);
|
||||
await updateSetting('SMS_Service', '');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await closeOmnichannelRoom(smsRoom);
|
||||
|
||||
await deleteVisitor(smsVisitor.token);
|
||||
|
||||
await updateSetting('SMS_Default_Omnichannel_Department', '');
|
||||
await updateSetting('SMS_Service', 'twilio');
|
||||
});
|
||||
|
||||
// Twilio sends the body as a x-www-form-urlencoded, the tests should do the same
|
||||
describe('POST livechat/sms-incoming/:service', () => {
|
||||
it('should throw an error if SMS is disabled', async () => {
|
||||
await updateSetting('SMS_Enabled', false);
|
||||
await request
|
||||
.post(api('livechat/sms-incoming/twilio'))
|
||||
.set(credentials)
|
||||
.send('from=%2B123456789&body=Hello')
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('should return an error when SMS service is not configured', async () => {
|
||||
await updateSetting('SMS_Enabled', true);
|
||||
await request
|
||||
.post(api('livechat/sms-incoming/twilio'))
|
||||
.set(credentials)
|
||||
.send('From=%2B123456789&To=%2B123456789&Body=Hello')
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('should throw an error if SMS_Default_Omnichannel_Department does not exists', async () => {
|
||||
await updateSetting('SMS_Default_Omnichannel_Department', '123');
|
||||
|
||||
await request
|
||||
.post(api('livechat/sms-incoming/twilio'))
|
||||
.set(credentials)
|
||||
.send('From=%2B123456789&To=%2B123456789&Body=Hello')
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400);
|
||||
});
|
||||
|
||||
it('should return headers and <Response> as body on success', async () => {
|
||||
await updateSetting('SMS_Default_Omnichannel_Department', '');
|
||||
await updateSetting('SMS_Service', 'twilio');
|
||||
|
||||
// Create visitor with the phone number that will be used in SMS
|
||||
smsVisitor = await createVisitor(undefined, 'sms-visitor', undefined, '+12345678910');
|
||||
|
||||
const response = await request
|
||||
.post(api('livechat/sms-incoming/twilio'))
|
||||
.set(credentials)
|
||||
.send('From=%2B12345678910&To=%2B123456789&Body=Hello')
|
||||
.expect('Content-Type', 'text/xml')
|
||||
.expect(200);
|
||||
|
||||
expect(response).to.have.property('text', '<Response></Response>');
|
||||
|
||||
const lastChat = (await getLivechatVisitorByToken(smsVisitor.token)).lastChat?._id;
|
||||
expect(lastChat).to.exist;
|
||||
|
||||
smsRoom = lastChat as string;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Livechat - Webhooks', () => {
|
||||
const webhookUrl = process.env.WEBHOOK_TEST_URL || 'https://httpbin.org';
|
||||
|
||||
describe('livechat/webhook.test', () => {
|
||||
it('should fail when user doesnt have view-livechat-webhooks permission', async () => {
|
||||
await updatePermission('view-livechat-webhooks', []);
|
||||
const response = await request.post(api('livechat/webhook.test')).set(credentials).expect(403);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if setting Livechat_webhookUrl is not set', async () => {
|
||||
await updateSetting('Livechat_webhookUrl', '');
|
||||
await updatePermission('view-livechat-webhooks', ['admin', 'livechat-manager']);
|
||||
const response = await request.post(api('livechat/webhook.test')).set(credentials).expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return true if webhook test went good', async () => {
|
||||
await updateSetting('Livechat_webhookUrl', `${webhookUrl}/status/200`);
|
||||
const response = await request.post(api('livechat/webhook.test')).set(credentials).expect(200);
|
||||
expect(response.body.success).to.be.true;
|
||||
});
|
||||
it('should fail if webhook test went bad', async () => {
|
||||
await updateSetting('Livechat_webhookUrl', `${webhookUrl}/status/400`);
|
||||
await request.post(api('livechat/webhook.test')).set(credentials).expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Webhook notifications', () => {
|
||||
const visitorsToDelete: ILivechatVisitor[] = [];
|
||||
const roomsToClose: IOmnichannelRoom[] = [];
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_webhookUrl', `${webhookUrl}/anything`);
|
||||
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
|
||||
await createAgent();
|
||||
});
|
||||
after(async () => {
|
||||
await Promise.allSettled(roomsToClose.map((room) => closeOmnichannelRoom(room._id)));
|
||||
await Promise.allSettled(visitorsToDelete.map((visitor) => deleteVisitor(visitor.token)));
|
||||
await updateSetting('Livechat_webhookUrl', '');
|
||||
await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
|
||||
await updateSetting('Livechat_webhook_on_start', false);
|
||||
await updateSetting('Livechat_webhook_on_close', false);
|
||||
await updateSetting('Livechat_webhook_on_chat_taken', false);
|
||||
await updateSetting('Livechat_webhook_on_chat_queued', false);
|
||||
});
|
||||
|
||||
it('should send a notification on chat start', async () => {
|
||||
await updateSetting('Livechat_webhook_on_start', true);
|
||||
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt();
|
||||
roomsToClose.push(room);
|
||||
visitorsToDelete.push(visitor);
|
||||
await sleep(1000);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.crmData).to.be.an('string');
|
||||
expect(JSON.parse(roomInfo.crmData as string))
|
||||
.to.have.property('json')
|
||||
.that.has.property('type', 'LivechatSessionStart');
|
||||
await updateSetting('Livechat_webhook_on_start', false);
|
||||
});
|
||||
it('should send a notification on chat taken', async () => {
|
||||
await updateSetting('Livechat_webhook_on_chat_taken', true);
|
||||
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt();
|
||||
roomsToClose.push(room);
|
||||
visitorsToDelete.push(visitor);
|
||||
await sleep(1000);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.crmData).to.be.an('string');
|
||||
expect(JSON.parse(roomInfo.crmData as string))
|
||||
.to.have.property('json')
|
||||
.that.has.property('type', 'LivechatSessionTaken');
|
||||
await updateSetting('Livechat_webhook_on_chat_taken', false);
|
||||
});
|
||||
let queuedRoom: IOmnichannelRoom;
|
||||
let queuedVisitor: ILivechatVisitor;
|
||||
it('should send a notification on chat queued', async () => {
|
||||
await updateSetting('Livechat_webhook_on_chat_queued', true);
|
||||
|
||||
queuedVisitor = await createVisitor();
|
||||
queuedRoom = await createLivechatRoom(queuedVisitor.token);
|
||||
roomsToClose.push(queuedRoom);
|
||||
visitorsToDelete.push(queuedVisitor);
|
||||
await sleep(1000);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(queuedRoom._id);
|
||||
|
||||
expect(roomInfo.crmData).to.be.an('string');
|
||||
expect(JSON.parse(roomInfo.crmData as string))
|
||||
.to.have.property('json')
|
||||
.that.has.property('type', 'LivechatSessionQueued');
|
||||
await updateSetting('Livechat_webhook_on_chat_queued', false);
|
||||
});
|
||||
it('should send a notification on chat close', async () => {
|
||||
await updateSetting('Livechat_webhook_on_close', true);
|
||||
|
||||
await closeOmnichannelRoom(queuedRoom._id);
|
||||
const idx = roomsToClose.indexOf(queuedRoom);
|
||||
if (idx > -1) {
|
||||
roomsToClose.splice(idx, 1);
|
||||
}
|
||||
|
||||
await sleep(1000);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(queuedRoom._id);
|
||||
|
||||
expect(roomInfo.crmData).to.be.an('string');
|
||||
expect(JSON.parse(roomInfo.crmData as string))
|
||||
.to.have.property('json')
|
||||
.that.has.property('type', 'LivechatSession');
|
||||
await updateSetting('Livechat_webhook_on_close', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('omnichannel/integrations', () => {
|
||||
describe('POST', () => {
|
||||
it('should update the integration settings if the required parameters are provided', async () => {
|
||||
const response = await request
|
||||
.post(api('omnichannel/integrations'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
LivechatWebhookUrl: 'http://localhost:8080',
|
||||
LivechatSecretToken: 'asdfasdf',
|
||||
LivechatHttpTimeout: 3000,
|
||||
LivechatWebhookOnStart: false,
|
||||
LivechatWebhookOnClose: false,
|
||||
LivechatWebhookOnChatTaken: false,
|
||||
LivechatWebhookOnChatQueued: false,
|
||||
LivechatWebhookOnForward: false,
|
||||
LivechatWebhookOnOfflineMsg: false,
|
||||
LivechatWebhookOnVisitorMessage: false,
|
||||
LivechatWebhookOnAgentMessage: false,
|
||||
})
|
||||
.expect(200);
|
||||
expect(response.body).to.have.property('success', true);
|
||||
});
|
||||
it('should fail if a wrong type is provided', async () => {
|
||||
const response = await request
|
||||
.post(api('omnichannel/integrations'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
LivechatWebhookUrl: 8000,
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(response.body).to.have.property('success', true);
|
||||
});
|
||||
it('should fail if a wrong setting is provided', async () => {
|
||||
const response = await request
|
||||
.post(api('omnichannel/integrations'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
LivechatWebhook_url: 'http://localhost:8000',
|
||||
})
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
1479
apps/meteor/tests/end-to-end/api/livechat/07-queue.ts
Normal file
1479
apps/meteor/tests/end-to-end/api/livechat/07-queue.ts
Normal file
File diff suppressed because it is too large
Load Diff
426
apps/meteor/tests/end-to-end/api/livechat/08-triggers.ts
Normal file
426
apps/meteor/tests/end-to-end/api/livechat/08-triggers.ts
Normal file
@@ -0,0 +1,426 @@
|
||||
import { expect } from 'chai';
|
||||
import { before, describe, it, after } from 'mocha';
|
||||
import type { Response } from 'supertest';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { createTrigger, fetchTriggers } from '../../../data/livechat/triggers';
|
||||
import { removePermissionFromAllRoles, restorePermissionToRoles, updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
describe('LIVECHAT - triggers', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
});
|
||||
|
||||
describe('livechat/triggers', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await removePermissionFromAllRoles('view-livechat-manager');
|
||||
await request.get(api('livechat/triggers')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
});
|
||||
|
||||
it('should return an array of triggers', async () => {
|
||||
await restorePermissionToRoles('view-livechat-manager');
|
||||
await createTrigger(`test${Date.now()}`);
|
||||
await request
|
||||
.get(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.triggers).to.be.an('array');
|
||||
expect(res.body).to.have.property('count').to.be.greaterThan(0);
|
||||
expect(res.body.triggers[0]).to.have.property('_id');
|
||||
expect(res.body.triggers[0]).to.have.property('name');
|
||||
expect(res.body.triggers[0]).to.have.property('description');
|
||||
expect(res.body.triggers[0]).to.have.property('enabled', true);
|
||||
expect(res.body.triggers[0]).to.have.property('runOnce').that.is.a('boolean');
|
||||
expect(res.body.triggers[0]).to.have.property('conditions').that.is.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(res.body.triggers[0]).to.have.property('actions').that.is.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body).to.have.property('total');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST livechat/triggers', () => {
|
||||
it('should fail if user is not logged in', async () => {
|
||||
await request.post(api('livechat/triggers')).send({}).expect(401);
|
||||
});
|
||||
it('should fail if no data is sent', async () => {
|
||||
await request.post(api('livechat/triggers')).set(credentials).send({}).expect(400);
|
||||
});
|
||||
it('should fail if invalid data is sent', async () => {
|
||||
await request.post(api('livechat/triggers')).set(credentials).send({ name: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if name is not an string', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 1, description: 'test', enabled: true, runOnce: true, conditions: [], actions: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if description is not an string', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', description: 1, enabled: true, runOnce: true, conditions: [], actions: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if enabled is not an boolean', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', description: 'test', enabled: 1, runOnce: true, conditions: [], actions: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if runOnce is not an boolean', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', description: 'test', enabled: true, runOnce: 1, conditions: [], actions: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if conditions is not an array', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', description: 'test', enabled: true, runOnce: true, conditions: 1, actions: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if actions is not an array', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', description: 'test', enabled: true, runOnce: true, conditions: [], actions: 1 })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if conditions is an array with invalid data', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', description: 'test', enabled: true, runOnce: true, conditions: [1], actions: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if conditions is an array of objects, but name is not a valid value', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', description: 'test', enabled: true, runOnce: true, conditions: [{ name: 'invalid' }], actions: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if actions is an array of invalid values', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', description: 'test', enabled: true, runOnce: true, conditions: [{ name: 'page-url' }], actions: [1] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if actions is an array of objects, but name is not a valid value', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test',
|
||||
description: 'test',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [{ name: 'invalid' }],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if actions is an array of objects, but sender is not a valid value', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test',
|
||||
description: 'test',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url' }],
|
||||
actions: [{ name: 'send-message', params: { sender: 'invalid' } }],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if actions is an array of objects, but msg is not a valid value', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test',
|
||||
description: 'test',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [{ name: 'send-message', params: { sender: 'custom' } }],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if actions is an array of objects, but name is not a valid value', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test',
|
||||
description: 'test',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [{ name: 'send-message', params: { sender: 'custom', msg: 'test', name: {} } }],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if user doesnt have view-livechat-manager permission', async () => {
|
||||
await removePermissionFromAllRoles('view-livechat-manager');
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test',
|
||||
description: 'test',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [{ name: 'send-message', params: { sender: 'custom', msg: 'test', name: 'test' } }],
|
||||
})
|
||||
.expect(403);
|
||||
});
|
||||
it('should save a new trigger of type send-message', async () => {
|
||||
await restorePermissionToRoles('view-livechat-manager');
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test',
|
||||
description: 'test',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [{ name: 'send-message', params: { sender: 'custom', msg: 'test', name: 'test' } }],
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
it('should fail if type is use-external-service but serviceUrl is not a present', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test2',
|
||||
description: 'test2',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [
|
||||
{
|
||||
name: 'use-external-service',
|
||||
params: {
|
||||
serviceTimeout: 5000,
|
||||
serviceFallbackMessage: 'Were sorry, we cannot complete your request',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if type is use-external-service but serviceTimeout is not a present', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test2',
|
||||
description: 'test2',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [
|
||||
{
|
||||
name: 'use-external-service',
|
||||
params: {
|
||||
serviceUrl: 'http://localhost:3000/api/vX',
|
||||
serviceFallbackMessage: 'Were sorry, we cannot complete your request',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if type is use-external-service but serviceFallbackMessage is not a present', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test2',
|
||||
description: 'test2',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [
|
||||
{
|
||||
name: 'use-external-service',
|
||||
params: {
|
||||
serviceUrl: 'http://localhost:3000/api/vX',
|
||||
serviceTimeout: 5000,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
it('should save a new trigger of type use-external-service', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: 'test3',
|
||||
description: 'test3',
|
||||
enabled: true,
|
||||
runOnce: true,
|
||||
conditions: [{ name: 'page-url', value: 'http://localhost:3000' }],
|
||||
actions: [
|
||||
{
|
||||
name: 'use-external-service',
|
||||
params: {
|
||||
serviceUrl: 'http://localhost:3000/api/vX',
|
||||
serviceTimeout: 5000,
|
||||
serviceFallbackMessage: 'Were sorry, we cannot complete your request',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
(IS_EE ? describe : describe.skip)('POST livechat/triggers/external-service/test', () => {
|
||||
const webhookUrl = process.env.WEBHOOK_TEST_URL || 'https://httpbin.org';
|
||||
|
||||
after(() => Promise.all([updateSetting('Livechat_secret_token', ''), restorePermissionToRoles('view-livechat-manager')]));
|
||||
|
||||
it('should fail if user is not logged in', async () => {
|
||||
await request.post(api('livechat/triggers/external-service/test')).send({}).expect(401);
|
||||
});
|
||||
it('should fail if no data is sent', async () => {
|
||||
await request.post(api('livechat/triggers/external-service/test')).set(credentials).send({}).expect(400);
|
||||
});
|
||||
it('should fail if invalid data is sent', async () => {
|
||||
await request.post(api('livechat/triggers/external-service/test')).set(credentials).send({ webhookUrl: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if webhookUrl is not an string', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: 1, timeout: 1000, fallbackMessage: 'test', extraData: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if timeout is not an number', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: 'test', timeout: '1000', fallbackMessage: 'test', extraData: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if fallbackMessage is not an string', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: 'test', timeout: 1000, fallbackMessage: 1, extraData: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if params is not an array', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: 'test', timeout: 1000, fallbackMessage: 'test', extraData: 1 })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if user doesnt have view-livechat-webhooks permission', async () => {
|
||||
await removePermissionFromAllRoles('view-livechat-manager');
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: 'test', timeout: 1000, fallbackMessage: 'test', extraData: [] })
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if Livechat_secret_token setting is empty', async () => {
|
||||
await restorePermissionToRoles('view-livechat-manager');
|
||||
await updateSetting('Livechat_secret_token', '');
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: 'test', timeout: 1000, fallbackMessage: 'test', extraData: [] })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return error when webhook returns error', async () => {
|
||||
await updateSetting('Livechat_secret_token', 'test');
|
||||
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: `${webhookUrl}/status/500`, timeout: 5000, fallbackMessage: 'test', extraData: [] })
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'error-invalid-external-service-response');
|
||||
expect(res.body).to.have.property('response').to.be.a('string');
|
||||
});
|
||||
});
|
||||
it('should return error when webhook times out', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: `${webhookUrl}/delay/2`, timeout: 1000, fallbackMessage: 'test', extraData: [] })
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'error-timeout');
|
||||
expect(res.body).to.have.property('response').to.be.a('string');
|
||||
});
|
||||
});
|
||||
it('should fail when webhook returns an answer that doesnt match the format', async () => {
|
||||
await request
|
||||
.post(api('livechat/triggers/external-service/test'))
|
||||
.set(credentials)
|
||||
.send({ webhookUrl: `${webhookUrl}/anything`, timeout: 5000, fallbackMessage: 'test', extraData: [] })
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'error-invalid-external-service-response');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/triggers/:id', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request.get(api('livechat/triggers/invalid-id')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
});
|
||||
it('should return null when trigger does not exist', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/triggers/invalid-id'))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.trigger).to.be.null;
|
||||
});
|
||||
it('should return the trigger', async () => {
|
||||
const triggerName = `test${Date.now()}`;
|
||||
await createTrigger(triggerName);
|
||||
const trigger = (await fetchTriggers()).find((t) => t.name === triggerName);
|
||||
const response = await request
|
||||
.get(api(`livechat/triggers/${trigger?._id}`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.trigger).to.be.an('object');
|
||||
expect(response.body.trigger).to.have.property('_id', trigger?._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
1680
apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts
Normal file
1680
apps/meteor/tests/end-to-end/api/livechat/09-visitors.ts
Normal file
File diff suppressed because it is too large
Load Diff
1022
apps/meteor/tests/end-to-end/api/livechat/10-departments.ts
Normal file
1022
apps/meteor/tests/end-to-end/api/livechat/10-departments.ts
Normal file
File diff suppressed because it is too large
Load Diff
215
apps/meteor/tests/end-to-end/api/livechat/11-email-inbox.ts
Normal file
215
apps/meteor/tests/end-to-end/api/livechat/11-email-inbox.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import type { IEmailInbox } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { after, before, describe, it } from 'mocha';
|
||||
import type { Response } from 'supertest';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { createEmailInbox } from '../../../data/livechat/inboxes';
|
||||
import { updatePermission } from '../../../data/permissions.helper';
|
||||
|
||||
describe('Email inbox', () => {
|
||||
before((done) => getCredentials(done));
|
||||
let testInbox = '';
|
||||
before(async () => {
|
||||
await request
|
||||
.post(api('email-inbox'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
active: true,
|
||||
name: 'test-email-inbox##',
|
||||
email: 'test-email@example.com',
|
||||
description: 'test email inbox',
|
||||
senderInfo: 'test email inbox',
|
||||
smtp: {
|
||||
server: 'smtp.example.com',
|
||||
port: 587,
|
||||
username: 'example@example.com',
|
||||
password: 'not-a-real-password',
|
||||
secure: true,
|
||||
},
|
||||
imap: {
|
||||
server: 'imap.example.com',
|
||||
port: 993,
|
||||
username: 'example@example.com',
|
||||
password: 'not-a-real-password',
|
||||
secure: true,
|
||||
maxRetries: 10,
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success');
|
||||
if (res.body.success === true) {
|
||||
testInbox = res.body._id;
|
||||
} else {
|
||||
expect(res.body).to.have.property('error');
|
||||
expect(res.body.error.includes('E11000')).to.be.eq(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
if (testInbox) {
|
||||
await request
|
||||
.delete(api(`email-inbox/${testInbox}`))
|
||||
.set(credentials)
|
||||
.send()
|
||||
.expect(200);
|
||||
}
|
||||
});
|
||||
describe('GET email-inbox.list', () => {
|
||||
it('should fail if user doesnt have manage-email-inbox permission', async () => {
|
||||
await updatePermission('manage-email-inbox', []);
|
||||
await request.get(api('email-inbox.list')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return a list of email inboxes', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
const res = await request.get(api('email-inbox.list')).set(credentials).send().expect('Content-Type', 'application/json').expect(200);
|
||||
|
||||
expect(res.body).to.have.property('emailInboxes');
|
||||
expect(res.body.emailInboxes).to.be.an('array');
|
||||
expect(res.body.emailInboxes).to.have.length.of.at.least(1);
|
||||
expect(res.body.emailInboxes.filter((ibx: IEmailInbox) => ibx.email === 'test-email@example.com')).to.have.length.gte(1);
|
||||
// make sure we delete the test inbox, even if creation failed on this test run
|
||||
testInbox = res.body.emailInboxes.filter((ibx: IEmailInbox) => ibx.email === 'test-email@example.com')[0]._id;
|
||||
expect(res.body).to.have.property('total');
|
||||
expect(res.body.total).to.be.a('number');
|
||||
expect(res.body).to.have.property('count');
|
||||
expect(res.body.count).to.be.a('number');
|
||||
expect(res.body).to.have.property('offset');
|
||||
expect(res.body.offset).to.be.a('number');
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST email-inbox', () => {
|
||||
let inboxId: string;
|
||||
const mockedPayload = {
|
||||
name: 'test',
|
||||
active: false,
|
||||
email: `test${new Date().getTime()}@test.com`,
|
||||
description: 'Updated test description',
|
||||
senderInfo: 'test',
|
||||
smtp: {
|
||||
server: 'smtp.example.com',
|
||||
port: 587,
|
||||
username: 'xxxx',
|
||||
password: 'xxxx',
|
||||
secure: true,
|
||||
},
|
||||
imap: {
|
||||
server: 'imap.example.com',
|
||||
port: 993,
|
||||
username: 'xxxx',
|
||||
password: 'xxxx',
|
||||
secure: true,
|
||||
maxRetries: 10,
|
||||
},
|
||||
};
|
||||
it('should fail if user doesnt have manage-email-inbox permission', async () => {
|
||||
await updatePermission('manage-email-inbox', []);
|
||||
await request.post(api('email-inbox')).set(credentials).send(mockedPayload).expect(403);
|
||||
});
|
||||
it('should fail if smtp config is not on body params', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
await request.post(api('email-inbox')).set(credentials).send({}).expect(400);
|
||||
});
|
||||
it('should fail if imap config is not on body params', async () => {
|
||||
const { imap, ...payload } = mockedPayload;
|
||||
await request.post(api('email-inbox')).set(credentials).send(payload).expect(400);
|
||||
});
|
||||
it('should fail if name is not on body params', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
const { name, ...payload } = mockedPayload;
|
||||
await request.post(api('email-inbox')).set(credentials).send(payload).expect(400);
|
||||
});
|
||||
it('should fail if active is not on body params', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
const { active, ...payload } = mockedPayload;
|
||||
await request.post(api('email-inbox')).set(credentials).send(payload).expect(400);
|
||||
});
|
||||
it('should fail if email is not on body params', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
const { email, ...payload } = mockedPayload;
|
||||
await request.post(api('email-inbox')).set(credentials).send(payload).expect(400);
|
||||
});
|
||||
it('should save an email inbox', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
const { email, ...payload } = mockedPayload;
|
||||
const { body } = await request
|
||||
.post(api('email-inbox'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
...payload,
|
||||
email: `test${new Date().getTime()}@test.com`,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('_id');
|
||||
inboxId = body._id;
|
||||
});
|
||||
it('should update an email inbox when _id is passed in the object', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
const { body } = await request
|
||||
.post(api('email-inbox'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
...mockedPayload,
|
||||
_id: inboxId,
|
||||
description: 'Updated test description',
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('_id');
|
||||
});
|
||||
});
|
||||
describe('GET email-inbox/:_id', () => {
|
||||
it('should fail if user doesnt have manage-email-inbox permission', async () => {
|
||||
await updatePermission('manage-email-inbox', []);
|
||||
await request.get(api('email-inbox/123')).set(credentials).expect(403);
|
||||
});
|
||||
it('should fail when email inbox does not exist', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
await request.get(api('email-inbox/123')).set(credentials).expect(404);
|
||||
});
|
||||
it('should return an email inbox', async () => {
|
||||
const inbox = await createEmailInbox();
|
||||
const { body } = await request
|
||||
.get(api(`email-inbox/${inbox._id}`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('_id');
|
||||
expect(body).to.have.property('name', 'test');
|
||||
});
|
||||
});
|
||||
describe('DELETE email-inbox/:_id', () => {
|
||||
it('should fail if user doesnt have manage-email-inbox permission', async () => {
|
||||
await updatePermission('manage-email-inbox', []);
|
||||
await request.delete(api('email-inbox/123')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return nothing when email inbox does not exist', async () => {
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
await request.delete(api('email-inbox/123')).set(credentials).expect(404);
|
||||
});
|
||||
it('should delete an email inbox', async () => {
|
||||
const inbox = await createEmailInbox();
|
||||
const { body } = await request
|
||||
.delete(api(`email-inbox/${inbox._id}`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
});
|
||||
});
|
||||
describe('GET email-inbox.search', () => {
|
||||
it('should fail if user doesnt have manage-email-inbox permission', async () => {
|
||||
await updatePermission('manage-email-inbox', []);
|
||||
await request.get(api('email-inbox.search')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return an email inbox matching email', async () => {
|
||||
await createEmailInbox();
|
||||
await updatePermission('manage-email-inbox', ['admin']);
|
||||
await request.get(api('email-inbox.search')).query({ email: 'test' }).set(credentials).expect(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
844
apps/meteor/tests/end-to-end/api/livechat/11-livechat.ts
Normal file
844
apps/meteor/tests/end-to-end/api/livechat/11-livechat.ts
Normal file
@@ -0,0 +1,844 @@
|
||||
import type { Credentials } from '@rocket.chat/api-client';
|
||||
import type { ILivechatDepartment, IUser } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { after, before, describe, it } from 'mocha';
|
||||
|
||||
import { sleep } from '../../../../lib/utils/sleep';
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { createCustomField, deleteCustomField } from '../../../data/livechat/custom-fields';
|
||||
import {
|
||||
addOrRemoveAgentFromDepartment,
|
||||
createDepartmentWithAnOfflineAgent,
|
||||
createDepartmentWithAnOnlineAgent,
|
||||
deleteDepartment,
|
||||
} from '../../../data/livechat/department';
|
||||
import {
|
||||
createVisitor,
|
||||
createLivechatRoom,
|
||||
makeAgentUnavailable,
|
||||
closeOmnichannelRoom,
|
||||
sendMessage,
|
||||
deleteVisitor,
|
||||
createDepartment,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import { createBotAgent, getRandomVisitorToken } from '../../../data/livechat/users';
|
||||
import type { WithRequiredProperty } from '../../../data/livechat/utils';
|
||||
import { removePermissionFromAllRoles, restorePermissionToRoles, updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import { deleteUser } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
describe('LIVECHAT - Utils', () => {
|
||||
before((done) => getCredentials(done));
|
||||
before(async () => {
|
||||
await updateSetting('Omnichannel_enable_department_removal', true);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
await updateSetting('Livechat_offline_email', '');
|
||||
await updateSetting('Omnichannel_enable_department_removal', false);
|
||||
});
|
||||
|
||||
describe('livechat/offline.message', () => {
|
||||
it('should fail if name is not sent as body parameter', async () => {
|
||||
const { body } = await request.post(api('livechat/offline.message')).set(credentials).send({});
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if email is not sent as body parameter', async () => {
|
||||
const { body } = await request.post(api('livechat/offline.message')).set(credentials).send({ name: 'test' });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if message is not sent as body parameter', async () => {
|
||||
await request.post(api('livechat/offline.message')).set(credentials).send({ name: 'test', email: '' }).expect(400);
|
||||
});
|
||||
it('should fail if setting Livechat_display_offline_form is disabled', async () => {
|
||||
await updateSetting('Livechat_display_offline_form', false);
|
||||
await request.post(api('livechat/offline.message')).set(credentials).send({ name: 'test', email: '', message: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if Livechat_validate_offline_email is enabled and email passed is not resolvable', async () => {
|
||||
await updateSetting('Livechat_display_offline_form', true);
|
||||
await updateSetting('Livechat_validate_offline_email', true);
|
||||
await request
|
||||
.post(api('livechat/offline.message'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', email: 'test@fadsjfldasjfd.com', message: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if setting Livechat_offline_email is not setup or is invalid', async () => {
|
||||
await updateSetting('Livechat_validate_offline_email', false);
|
||||
await updateSetting('Livechat_validate_offline_email', 'afsdxcvxc');
|
||||
await request
|
||||
.post(api('livechat/offline.message'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', email: 'test@email.com', message: 'this is a test :)' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should send an offline email', async () => {
|
||||
await updateSetting('Livechat_validate_offline_email', false);
|
||||
await updateSetting('Livechat_offline_email', 'test-email@rocket.chat');
|
||||
await request
|
||||
.post(api('livechat/offline.message'))
|
||||
.set(credentials)
|
||||
.send({ name: 'test', email: 'test@email.com', message: 'this is a test :)' })
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/config', () => {
|
||||
let emptyDepartment: ILivechatDepartment;
|
||||
let forwardDepartment: ILivechatDepartment;
|
||||
let testDepartment: ILivechatDepartment;
|
||||
let agent: { user: WithRequiredProperty<IUser, 'username'>; credentials: Credentials };
|
||||
let agent2: { user: WithRequiredProperty<IUser, 'username'>; credentials: Credentials };
|
||||
|
||||
before(async () => {
|
||||
if (!IS_EE) {
|
||||
return;
|
||||
}
|
||||
|
||||
emptyDepartment = await createDepartment();
|
||||
({ department: forwardDepartment, agent } = await createDepartmentWithAnOnlineAgent());
|
||||
({ department: testDepartment, agent: agent2 } = await createDepartmentWithAnOfflineAgent({
|
||||
departmentsAllowedToForward: [forwardDepartment._id],
|
||||
}));
|
||||
});
|
||||
|
||||
after(() => {
|
||||
if (!IS_EE) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
deleteDepartment(emptyDepartment._id),
|
||||
deleteDepartment(forwardDepartment._id),
|
||||
deleteDepartment(testDepartment._id),
|
||||
deleteUser(agent.user),
|
||||
deleteUser(agent2.user),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return enabled: false if livechat is disabled', async () => {
|
||||
await updateSetting('Livechat_enabled', false);
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials);
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.have.property('enabled', false);
|
||||
});
|
||||
it('should return basic livechat config when enabled', async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials);
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.have.property('enabled', true);
|
||||
expect(body.config).to.have.property('settings');
|
||||
expect(body.config).to.have.property('departments').that.is.an('array');
|
||||
});
|
||||
it('should have custom fields data', async () => {
|
||||
const customFieldName = `new_custom_field_${Date.now()}`;
|
||||
await createCustomField({
|
||||
searchable: true,
|
||||
field: customFieldName,
|
||||
label: customFieldName,
|
||||
defaultValue: 'test_default_address',
|
||||
scope: 'visitor',
|
||||
visibility: 'visible',
|
||||
regexp: '',
|
||||
public: true,
|
||||
required: false,
|
||||
options: '',
|
||||
});
|
||||
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials);
|
||||
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.have.property('customFields').that.is.an('array');
|
||||
expect(body.config.customFields).to.have.length.greaterThan(0);
|
||||
const customField = body.config.customFields.find((field: any) => field._id === customFieldName);
|
||||
expect(customField).to.be.an('object');
|
||||
expect(customField).to.have.property('label', customFieldName);
|
||||
|
||||
await deleteCustomField(customFieldName);
|
||||
});
|
||||
(IS_EE ? it : it.skip)('should return online as true if there is at least one agent online', async () => {
|
||||
const { department } = await createDepartmentWithAnOnlineAgent();
|
||||
|
||||
const { body } = await request.get(api('livechat/config')).query({ department: department._id }).set(credentials);
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.have.property('online', true);
|
||||
});
|
||||
(IS_EE ? it : it.skip)('should return online as false if there is no agent online', async () => {
|
||||
const { department, agent } = await createDepartmentWithAnOnlineAgent();
|
||||
await makeAgentUnavailable(agent.credentials);
|
||||
|
||||
const { body } = await request.get(api('livechat/config')).query({ department: department._id }).set(credentials);
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.have.property('online', false);
|
||||
});
|
||||
(IS_EE ? it : it.skip)('should return online as true if bot is online and there is no agent online', async () => {
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', true);
|
||||
|
||||
const { department, agent } = await createDepartmentWithAnOnlineAgent();
|
||||
await makeAgentUnavailable(agent.credentials);
|
||||
|
||||
const botUser = await createBotAgent();
|
||||
await addOrRemoveAgentFromDepartment(department._id, { agentId: botUser.user._id, username: botUser.user.username as string }, true);
|
||||
|
||||
const { body } = await request.get(api('livechat/config')).query({ department: department._id }).set(credentials);
|
||||
expect(body).to.have.property('config');
|
||||
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', false);
|
||||
await makeAgentUnavailable(botUser.credentials);
|
||||
});
|
||||
it('should return a guest if there exists a guest with the same token', async () => {
|
||||
const guest = await createVisitor();
|
||||
const { body } = await request.get(api('livechat/config')).query({ token: guest.token }).set(credentials);
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.have.property('guest');
|
||||
expect(body.config.guest).to.have.property('name', guest.name);
|
||||
});
|
||||
it('should not return a guest if there exists a guest with the same token but the guest is not online', async () => {
|
||||
const token = getRandomVisitorToken();
|
||||
|
||||
const { body } = await request.get(api('livechat/config')).query({ token }).set(credentials);
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.not.have.property('guest');
|
||||
});
|
||||
it('should return no online room if visitor is not chatting with an agent', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const { body } = await request.get(api('livechat/config')).query({ token: visitor.token }).set(credentials);
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.not.have.property('room');
|
||||
});
|
||||
it('should return online room if visitor is already chatting with an agent', async () => {
|
||||
const newVisitor = await createVisitor();
|
||||
const newRoom = await createLivechatRoom(newVisitor.token);
|
||||
|
||||
const { body } = await request.get(api('livechat/config')).query({ token: newVisitor.token }).set(credentials);
|
||||
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.have.property('room');
|
||||
expect(body.config.room).to.have.property('_id', newRoom._id);
|
||||
});
|
||||
(IS_EE ? it : it.skip)('should return list of departments with at least one agent', async () => {
|
||||
const { body } = await request.get(api('livechat/config')).set(credentials);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('config');
|
||||
expect(body.config).to.have.property('departments');
|
||||
expect(body.config.departments).to.be.an('array').with.lengthOf.at.least(2);
|
||||
|
||||
expect(body.config.departments).to.not.deep.include({
|
||||
_id: emptyDepartment._id,
|
||||
name: emptyDepartment.name,
|
||||
showOnRegistration: emptyDepartment.showOnRegistration,
|
||||
showOnOfflineForm: emptyDepartment.showOnOfflineForm,
|
||||
});
|
||||
expect(body.config.departments).to.deep.include({
|
||||
_id: forwardDepartment._id,
|
||||
name: forwardDepartment.name,
|
||||
showOnRegistration: forwardDepartment.showOnRegistration,
|
||||
showOnOfflineForm: forwardDepartment.showOnOfflineForm,
|
||||
});
|
||||
expect(body.config.departments).to.deep.include({
|
||||
_id: testDepartment._id,
|
||||
name: testDepartment.name,
|
||||
showOnRegistration: testDepartment.showOnRegistration,
|
||||
showOnOfflineForm: testDepartment.showOnOfflineForm,
|
||||
departmentsAllowedToForward: [forwardDepartment._id],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/page.visited', () => {
|
||||
it('should fail if token is not sent as body param', async () => {
|
||||
const { body } = await request.post(api('livechat/page.visited')).set(credentials).send({});
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if rid is not an string', async () => {
|
||||
const { body } = await request.post(api('livechat/page.visited')).set(credentials).send({ token: 'test', rid: {} });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if pageInfo is not sent as body param', async () => {
|
||||
const { body } = await request.post(api('livechat/page.visited')).set(credentials).send({ token: 'test', rid: 'test' });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if pageInfo is not of the right format', async () => {
|
||||
const { body } = await request.post(api('livechat/page.visited')).set(credentials).send({ token: 'test', rid: 'test', pageInfo: {} });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should return empty if pageInfo.change is not equal to Livechat_history_monitor_type', async () => {
|
||||
const { body } = await request
|
||||
.post(api('livechat/page.visited'))
|
||||
.set(credentials)
|
||||
.send({ token: 'test', rid: 'test', pageInfo: { change: 'test', title: 'test', location: { href: 'test' } } });
|
||||
expect(body).to.have.property('success', true);
|
||||
});
|
||||
it('should store values for 1 month if visitor has no room yet', async () => {
|
||||
const { body } = await request
|
||||
.post(api('livechat/page.visited'))
|
||||
.set(credentials)
|
||||
.send({ token: 'test', rid: 'test', pageInfo: { change: 'url', title: 'Rocket.Chat', location: { href: 'https://rocket.chat' } } });
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('page');
|
||||
expect(body.page).to.have.property('navigation');
|
||||
expect(body.page).to.have.property('msg');
|
||||
});
|
||||
it('should store values correctly on visitor room when room is valid', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/page.visited'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
token: visitor.token,
|
||||
rid: room._id,
|
||||
pageInfo: { change: 'url', title: 'Rocket.Chat', location: { href: 'https://rocket.chat' } },
|
||||
});
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('page');
|
||||
expect(body.page).to.have.property('navigation');
|
||||
expect(body.page).to.have.property('msg');
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/transcript', () => {
|
||||
it('should fail if token is not in body params', async () => {
|
||||
const { body } = await request.post(api('livechat/transcript')).set(credentials).send({});
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if rid is not in body params', async () => {
|
||||
const { body } = await request.post(api('livechat/transcript')).set(credentials).send({ token: 'test' });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if email is not in body params', async () => {
|
||||
const { body } = await request.post(api('livechat/transcript')).set(credentials).send({ token: 'test', rid: 'test' });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if token is not a valid guest token', async () => {
|
||||
const { body } = await request.post(api('livechat/transcript')).set(credentials).send({ token: 'test', rid: 'test', email: '' });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if rid is not a valid room id', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const { body } = await request
|
||||
.post(api('livechat/transcript'))
|
||||
.set(credentials)
|
||||
.send({ token: visitor.token, rid: 'test', email: '' });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if requesting a transcript of another visitors room', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const visitor2 = await createVisitor();
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/transcript'))
|
||||
.set(credentials)
|
||||
.send({ token: visitor2.token, rid: room._id, email: '' });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if email is not a valid email', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/transcript'))
|
||||
.set(credentials)
|
||||
.send({ token: visitor.token, rid: room._id, email: 'test' });
|
||||
expect(body).to.have.property('success', false);
|
||||
});
|
||||
it('should send a transcript if all is good', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/transcript'))
|
||||
.set(credentials)
|
||||
.send({ token: visitor.token, rid: room._id, email: 'visitor@notadomain.com' });
|
||||
expect(body).to.have.property('success', true);
|
||||
});
|
||||
it('should allow a visitor to get a transcript even if token changed by using an old token that matches room.v', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
const visitor2 = await createVisitor(undefined, undefined, visitor.visitorEmails?.[0].address);
|
||||
const room2 = await createLivechatRoom(visitor2.token);
|
||||
await closeOmnichannelRoom(room2._id);
|
||||
|
||||
expect(visitor.token !== visitor2.token).to.be.true;
|
||||
const { body } = await request
|
||||
.post(api('livechat/transcript'))
|
||||
.set(credentials)
|
||||
.send({ token: visitor.token, rid: room._id, email: 'visitor@notadomain.com' });
|
||||
expect(body).to.have.property('success', true);
|
||||
|
||||
const { body: body2 } = await request
|
||||
.post(api('livechat/transcript'))
|
||||
.set(credentials)
|
||||
.send({ token: visitor2.token, rid: room2._id, email: 'visitor@notadomain.com' });
|
||||
expect(body2).to.have.property('success', true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/transcript/:rid', () => {
|
||||
it('should fail if user is not authenticated', async () => {
|
||||
await request.delete(api('livechat/transcript/rid')).send({}).expect(401);
|
||||
});
|
||||
it('should fail if user doesnt have "send-omnichannel-chat-transcript" permission', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
await removePermissionFromAllRoles('send-omnichannel-chat-transcript');
|
||||
|
||||
await request
|
||||
.delete(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({})
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if rid is not a valid room id', async () => {
|
||||
await restorePermissionToRoles('send-omnichannel-chat-transcript');
|
||||
await request.delete(api('livechat/transcript/rid')).set(credentials).send({}).expect(400);
|
||||
});
|
||||
it('should fail if room is not open', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await request
|
||||
.delete(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if room doesnt have transcript requested', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
|
||||
await request
|
||||
.delete(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({})
|
||||
.expect(400);
|
||||
});
|
||||
it('should remove transcript if all good', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
|
||||
await request
|
||||
.post(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({ email: 'abc@abc.com', subject: 'test' })
|
||||
.expect(200);
|
||||
|
||||
await request
|
||||
.delete(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({})
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST livechat/transcript/:rid', () => {
|
||||
it('should fail if user is not authenticated', async () => {
|
||||
await request.post(api('livechat/transcript/rid')).send({}).expect(401);
|
||||
});
|
||||
it('should fail if "email" param is not sent', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
|
||||
await request
|
||||
.post(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({})
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if "subject" param is not sent', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
|
||||
await request
|
||||
.post(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({ email: 'abc@abc.xmz' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if user doesnt have "send-omnichannel-chat-transcript" permission', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
await updatePermission('send-omnichannel-chat-transcript', []);
|
||||
|
||||
await request
|
||||
.post(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({ email: 'abc@abc.com', subject: 'test' })
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if rid is not a valid room id', async () => {
|
||||
await updatePermission('send-omnichannel-chat-transcript', ['livechat-manager', 'admin']);
|
||||
await request.post(api('livechat/transcript/rid')).set(credentials).send({ email: 'abc@abc.com', subject: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if room is not open', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await request
|
||||
.post(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({ email: 'abc@abc.com', subject: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if room already has transcript requested', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
|
||||
await request
|
||||
.post(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({ email: 'abc@abc.com', subject: 'test' })
|
||||
.expect(200);
|
||||
|
||||
await request
|
||||
.post(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({ email: 'abc@abc.com', subject: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should request transcript if all good', async () => {
|
||||
const user = await createVisitor();
|
||||
const room = await createLivechatRoom(user.token);
|
||||
|
||||
await request
|
||||
.post(api(`livechat/transcript/${room._id}`))
|
||||
.set(credentials)
|
||||
.send({ email: 'abc@abc.com', subject: 'test' })
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/visitors.search', () => {
|
||||
it('should bring sorted data by last chat time', async () => {
|
||||
const visitor1 = await createVisitor(undefined, 'VisitorInPast');
|
||||
const room1 = await createLivechatRoom(visitor1.token);
|
||||
|
||||
const visitor2 = await createVisitor(undefined, 'VisitorInPresent');
|
||||
const room2 = await createLivechatRoom(visitor2.token);
|
||||
|
||||
const { body: result1 } = await request
|
||||
.get(api('livechat/visitors.search'))
|
||||
.query({ term: 'VisitorIn', sort: '{"lastChat.ts":1}' })
|
||||
.set(credentials)
|
||||
.send();
|
||||
|
||||
expect(result1).to.have.property('visitors').that.is.an('array');
|
||||
expect(result1.visitors[0]).to.have.property('name');
|
||||
expect(result1.visitors[0].name).to.be.eq('VisitorInPast');
|
||||
|
||||
const { body: result2 } = await request
|
||||
.get(api('livechat/visitors.search'))
|
||||
.query({ term: 'VisitorIn', sort: '{"lastChat.ts":-1}' })
|
||||
.set(credentials)
|
||||
.send();
|
||||
|
||||
expect(result2).to.have.property('visitors').that.is.an('array');
|
||||
expect(result2.visitors[0]).to.have.property('name');
|
||||
expect(result2.visitors[0].name).to.be.eq('VisitorInPresent');
|
||||
|
||||
await closeOmnichannelRoom(room1._id);
|
||||
await closeOmnichannelRoom(room2._id);
|
||||
await deleteVisitor(visitor1.token);
|
||||
await deleteVisitor(visitor2.token);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/message', () => {
|
||||
const visitorTokens: string[] = [];
|
||||
|
||||
after(() => Promise.all(visitorTokens.map((token) => deleteVisitor(token))));
|
||||
|
||||
it('should fail if no token', async () => {
|
||||
await request.post(api('livechat/message')).set(credentials).send({}).expect(400);
|
||||
});
|
||||
it('should fail if no rid', async () => {
|
||||
await request.post(api('livechat/message')).set(credentials).send({ token: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if no msg', async () => {
|
||||
await request.post(api('livechat/message')).set(credentials).send({ token: 'test', rid: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if token is invalid', async () => {
|
||||
await request.post(api('livechat/message')).set(credentials).send({ token: 'test', rid: 'test', msg: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if rid is invalid', async () => {
|
||||
const visitor = await createVisitor();
|
||||
visitorTokens.push(visitor.token);
|
||||
await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: 'test', msg: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if rid belongs to another visitor', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const visitor2 = await createVisitor();
|
||||
visitorTokens.push(visitor.token, visitor2.token);
|
||||
|
||||
const room = await createLivechatRoom(visitor2.token);
|
||||
await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: room._id, msg: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if room is closed', async () => {
|
||||
const visitor = await createVisitor();
|
||||
visitorTokens.push(visitor.token);
|
||||
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: room._id, msg: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if message is greater than Livechat_enable_message_character_limit setting', async () => {
|
||||
const visitor = await createVisitor();
|
||||
visitorTokens.push(visitor.token);
|
||||
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await updateSetting('Livechat_enable_message_character_limit', true);
|
||||
await updateSetting('Livechat_message_character_limit', 1);
|
||||
await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: room._id, msg: 'test' }).expect(400);
|
||||
|
||||
await updateSetting('Livechat_enable_message_character_limit', false);
|
||||
});
|
||||
it('should send a message', async () => {
|
||||
const visitor = await createVisitor();
|
||||
visitorTokens.push(visitor.token);
|
||||
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await request.post(api('livechat/message')).set(credentials).send({ token: visitor.token, rid: room._id, msg: 'test' }).expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[GET] livechat/message/:_id', () => {
|
||||
it('should fail if no token is provided', async () => {
|
||||
await request.get(api('livechat/message/rid')).set(credentials).expect(400);
|
||||
});
|
||||
it('should fail if no rid is provided', async () => {
|
||||
await request.get(api('livechat/message/mid')).query({ token: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if token points to an invalid visitor', async () => {
|
||||
await request.get(api('livechat/message/mid')).query({ token: 'test', rid: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if room points to an invalid room', async () => {
|
||||
const visitor = await createVisitor();
|
||||
await request.get(api('livechat/message/mid')).query({ token: visitor.token, rid: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if _id points to an invalid message', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await request.get(api('livechat/message/mid')).query({ token: visitor.token, rid: room._id }).expect(400);
|
||||
});
|
||||
it('should return a message', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const message = await sendMessage(room._id, 'test', visitor.token);
|
||||
await request
|
||||
.get(api(`livechat/message/${message._id}`))
|
||||
.query({ token: visitor.token, rid: room._id })
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[PUT] livechat/message/:_id', () => {
|
||||
it('should fail if no token is provided', async () => {
|
||||
await request.put(api('livechat/message/rid')).set(credentials).expect(400);
|
||||
});
|
||||
it('should fail if no rid is provided', async () => {
|
||||
await request.put(api('livechat/message/mid')).query({ token: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if token points to an invalid visitor', async () => {
|
||||
await request.put(api('livechat/message/mid')).query({ token: 'test', rid: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if room points to an invalid room', async () => {
|
||||
const visitor = await createVisitor();
|
||||
await request.put(api('livechat/message/mid')).query({ token: visitor.token, rid: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if _id points to an invalid message', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await request.put(api('livechat/message/mid')).query({ token: visitor.token, rid: room._id }).expect(400);
|
||||
});
|
||||
it('should update a message', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const message = await sendMessage(room._id, 'test', visitor.token);
|
||||
const { body } = await request
|
||||
.put(api(`livechat/message/${message._id}`))
|
||||
.set(credentials)
|
||||
.send({ msg: 'test2', token: visitor.token, rid: room._id })
|
||||
.expect(200);
|
||||
expect(body).to.have.property('message');
|
||||
expect(body.message).to.have.property('_id', message._id);
|
||||
expect(body.message).to.have.property('msg', 'test2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('[DELETE] livechat/message/:_id', () => {
|
||||
it('should fail if no token is provided', async () => {
|
||||
await request.delete(api('livechat/message/rid')).set(credentials).expect(400);
|
||||
});
|
||||
it('should fail if no rid is provided', async () => {
|
||||
await request.delete(api('livechat/message/mid')).query({ token: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if token points to an invalid visitor', async () => {
|
||||
await request.delete(api('livechat/message/mid')).query({ token: 'test', rid: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if room points to an invalid room', async () => {
|
||||
const visitor = await createVisitor();
|
||||
await request.delete(api('livechat/message/mid')).query({ token: visitor.token, rid: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if _id points to an invalid message', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await request.delete(api('livechat/message/mid')).query({ token: visitor.token, rid: room._id }).expect(400);
|
||||
});
|
||||
it('should delete a message', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const message = await sendMessage(room._id, 'test', visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.delete(api(`livechat/message/${message._id}`))
|
||||
.send({ token: visitor.token, rid: room._id })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('message');
|
||||
expect(body.message).to.have.property('_id', message._id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/messages.history/:rid', () => {
|
||||
it('should fail if no token is provided', async () => {
|
||||
await request.get(api('livechat/messages.history/rid')).set(credentials).expect(400);
|
||||
});
|
||||
it('should fail if token points to an invalid visitor', async () => {
|
||||
await request.get(api('livechat/messages.history/rid')).query({ token: 'test' }).expect(400);
|
||||
});
|
||||
it('should fail if room points to an invalid room', async () => {
|
||||
const visitor = await createVisitor();
|
||||
await request.get(api('livechat/messages.history/rid')).query({ token: visitor.token }).expect(400);
|
||||
});
|
||||
it('should fail if room points to a room of another visitor', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const visitor2 = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor2.token);
|
||||
await request
|
||||
.get(api(`livechat/messages.history/${room._id}`))
|
||||
.query({ token: visitor.token })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return a list of messages', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await sendMessage(room._id, 'test', visitor.token);
|
||||
const { body } = await request
|
||||
.get(api(`livechat/messages.history/${room._id}`))
|
||||
.query({ token: visitor.token })
|
||||
.expect(200);
|
||||
expect(body).to.have.property('messages').that.is.an('array');
|
||||
expect(body.messages).to.have.lengthOf(2);
|
||||
expect(body.messages[0]).to.have.property('msg', 'test');
|
||||
});
|
||||
it('should return a list of messages with offset and count', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await sendMessage(room._id, 'test', visitor.token);
|
||||
await sendMessage(room._id, 'test2', visitor.token);
|
||||
const { body } = await request
|
||||
.get(api(`livechat/messages.history/${room._id}`))
|
||||
.query({ token: visitor.token, offset: 1, limit: 1 })
|
||||
.expect(200);
|
||||
expect(body).to.have.property('messages').that.is.an('array');
|
||||
expect(body.messages).to.have.lengthOf(1);
|
||||
expect(body.messages[0]).to.have.property('msg', 'test');
|
||||
});
|
||||
it('should return a list of unseen messages', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await sendMessage(room._id, 'test', visitor.token);
|
||||
await sendMessage(room._id, 'test2', visitor.token);
|
||||
const { body } = await request
|
||||
.get(api(`livechat/messages.history/${room._id}`))
|
||||
.query({ token: visitor.token, ls: new Date() })
|
||||
.expect(200);
|
||||
expect(body).to.have.property('messages').that.is.an('array');
|
||||
expect(body.messages).to.have.lengthOf(3);
|
||||
expect(body.messages[0]).to.have.property('msg', 'test2');
|
||||
});
|
||||
it('should return a list of messages up to a specific date', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const msg = await sendMessage(room._id, 'test', visitor.token);
|
||||
const tsPlusSomeMillis = new Date(new Date(msg.ts).getTime() + 500);
|
||||
await sleep(1000);
|
||||
await sendMessage(room._id, 'test2', visitor.token);
|
||||
const { body } = await request
|
||||
.get(api(`livechat/messages.history/${room._id}`))
|
||||
.query({ token: visitor.token, end: tsPlusSomeMillis })
|
||||
.expect(200);
|
||||
expect(body).to.have.property('messages').that.is.an('array');
|
||||
expect(body.messages).to.have.lengthOf(2);
|
||||
expect(body.messages[0]).to.have.property('msg', 'test');
|
||||
});
|
||||
it('should return message history for a valid room with pagination', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await sendMessage(room._id, 'Hello', visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.get(api(`livechat/messages.history/${room._id}`))
|
||||
.set(credentials)
|
||||
.query({ token: visitor.token, limit: 1 })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('messages').of.length(1);
|
||||
expect(body.messages[0]).to.have.property('msg', 'Hello');
|
||||
});
|
||||
it('should return message history for a valid room with pagination and offset', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await sendMessage(room._id, 'Hello', visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.get(api(`livechat/messages.history/${room._id}`))
|
||||
.set(credentials)
|
||||
.query({ token: visitor.token, limit: 1, offset: 1 })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body).to.have.property('messages').of.length(1);
|
||||
expect(body.messages[0]).to.have.property('t');
|
||||
});
|
||||
});
|
||||
|
||||
(IS_EE ? describe : describe.skip)('[EE] livechat widget', () => {
|
||||
it('should include additional css when provided via Livechat_WidgetLayoutClasses setting', async () => {
|
||||
await updateSetting('Livechat_WidgetLayoutClasses', 'http://my.css.com/my.css');
|
||||
const x = await request.get('/livechat').expect(200);
|
||||
|
||||
expect(x.text.includes('http://my.css.com/my.css')).to.be.true;
|
||||
});
|
||||
|
||||
it('should remove additional css when setting Livechat_WidgetLayoutClasses is empty', async () => {
|
||||
await updateSetting('Livechat_WidgetLayoutClasses', '');
|
||||
const x = await request.get('/livechat').expect(200);
|
||||
|
||||
expect(x.text.includes('http://my.css.com/my.css')).to.be.false;
|
||||
});
|
||||
|
||||
it('should include additional js when provided via Livechat_AdditionalWidgetScripts setting', async () => {
|
||||
await updateSetting('Livechat_AdditionalWidgetScripts', 'http://my.js.com/my.js');
|
||||
const x = await request.get('/livechat').expect(200);
|
||||
|
||||
expect(x.text.includes('http://my.js.com/my.js')).to.be.true;
|
||||
});
|
||||
|
||||
it('should remove additional js when setting Livechat_AdditionalWidgetScripts is empty', async () => {
|
||||
await updateSetting('Livechat_AdditionalWidgetScripts', '');
|
||||
const x = await request.get('/livechat').expect(200);
|
||||
|
||||
expect(x.text.includes('http://my.js.com/my.js')).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
135
apps/meteor/tests/end-to-end/api/livechat/12-mailer.ts
Normal file
135
apps/meteor/tests/end-to-end/api/livechat/12-mailer.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { expect } from 'chai';
|
||||
import { before, after, describe, it } from 'mocha';
|
||||
import type { Response } from 'supertest';
|
||||
|
||||
import { api, request, credentials, getCredentials } from '../../../data/api-data';
|
||||
import { updatePermission } from '../../../data/permissions.helper';
|
||||
|
||||
describe('Mailer', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
describe('POST mailer', async () => {
|
||||
before(async () => {
|
||||
return updatePermission('send-mail', ['admin']);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
return updatePermission('send-mail', ['admin']);
|
||||
});
|
||||
|
||||
it('should send an email if the payload is correct', async () => {
|
||||
await request
|
||||
.post(api('mailer'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
from: 'rocketchat.internal.admin.test@rocket.chat',
|
||||
subject: 'Test email subject',
|
||||
body: 'Test email body [unsubscribe]',
|
||||
dryrun: true,
|
||||
query: '',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
});
|
||||
it('should throw an error if the request is incorrect', async () => {
|
||||
await request
|
||||
.post(api('mailer'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
from: 12345,
|
||||
subject: {},
|
||||
body: 'Test email body',
|
||||
dryrun: true,
|
||||
query: '',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
});
|
||||
});
|
||||
it('should throw an error if the "from" param is missing', async () => {
|
||||
await request
|
||||
.post(api('mailer'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
subject: 'Test email subject',
|
||||
body: 'Test email body',
|
||||
dryrun: true,
|
||||
query: '',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
});
|
||||
});
|
||||
it('should throw an error if user does NOT have the send-mail permission', async () => {
|
||||
await updatePermission('send-mail', []);
|
||||
await request
|
||||
.post(api('mailer'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
from: 'test-mail@test.com',
|
||||
subject: 'Test email subject',
|
||||
body: 'Test email body',
|
||||
dryrun: true,
|
||||
query: '',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mailer Unsubscribe', () => {
|
||||
describe('POST mailer unsubscribe', () => {
|
||||
it('should unsubscribe to mailer if the request is correct', async () => {
|
||||
await request
|
||||
.post(api('mailer.unsubscribe'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
_id: credentials['X-User-Id'],
|
||||
createdAt: new Date().getTime().toString(),
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
});
|
||||
it('should throw an error if the "_id" param is missing', async () => {
|
||||
await request
|
||||
.post(api('mailer.unsubscribe'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
createdAt: new Date().getTime().toString(),
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
});
|
||||
});
|
||||
it('should throw an error if the "createdAt" param is missing', async () => {
|
||||
await request
|
||||
.post(api('mailer.unsubscribe'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
_id: credentials['X-User-Id'],
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
936
apps/meteor/tests/end-to-end/api/livechat/12-priorites.ts
Normal file
936
apps/meteor/tests/end-to-end/api/livechat/12-priorites.ts
Normal file
@@ -0,0 +1,936 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
242
apps/meteor/tests/end-to-end/api/livechat/13-tags.ts
Normal file
242
apps/meteor/tests/end-to-end/api/livechat/13-tags.ts
Normal file
@@ -0,0 +1,242 @@
|
||||
import type { ILivechatDepartment, ILivechatTag } 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 { saveTags, removeAllTags } from '../../../data/livechat/tags';
|
||||
import { createMonitor, createUnit } from '../../../data/livechat/units';
|
||||
import { removePermissionFromAllRoles, restorePermissionToRoles, updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import type { IUserWithCredentials } from '../../../data/user';
|
||||
import { password } from '../../../data/user';
|
||||
import { createUser, deleteUser, login } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
(IS_EE ? describe : describe.skip)('[EE] Livechat - Tags', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
});
|
||||
|
||||
describe('livechat/tags', () => {
|
||||
let tagsData: {
|
||||
caseA: { department: ILivechatDepartment; tag: ILivechatTag; agent: IUserWithCredentials };
|
||||
caseB: { department: ILivechatDepartment; tag: ILivechatTag; agent: IUserWithCredentials };
|
||||
casePublic: { tag: ILivechatTag };
|
||||
};
|
||||
let monitor: IUserWithCredentials;
|
||||
|
||||
before(async () => {
|
||||
// create monitor
|
||||
const monitorUser = await createUser();
|
||||
const monitorCredentials = await login(monitorUser.username, password);
|
||||
await createMonitor(monitorUser.username);
|
||||
monitor = {
|
||||
user: monitorUser,
|
||||
credentials: monitorCredentials,
|
||||
};
|
||||
|
||||
// remove all existing tags
|
||||
await removeAllTags();
|
||||
|
||||
// should add 3 tags
|
||||
const { department: dA, agent: agentA } = await createDepartmentWithAnOnlineAgent();
|
||||
const tagA = await saveTags([dA._id]);
|
||||
const { department: dB, agent: agentB } = await createDepartmentWithAnOnlineAgent();
|
||||
const tagB = await saveTags([dB._id]);
|
||||
const publicTag = await saveTags();
|
||||
tagsData = {
|
||||
caseA: { department: dA, tag: tagA, agent: agentA },
|
||||
caseB: { department: dB, tag: tagB, agent: agentB },
|
||||
casePublic: { tag: publicTag },
|
||||
};
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(monitor.user);
|
||||
await removeAllTags();
|
||||
});
|
||||
|
||||
it('should throw unauthorized error when the user does not have the necessary permission', async () => {
|
||||
await removePermissionFromAllRoles('manage-livechat-tags');
|
||||
await removePermissionFromAllRoles('view-l-room');
|
||||
const response = await request.get(api('livechat/tags')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
|
||||
await restorePermissionToRoles('manage-livechat-tags');
|
||||
await restorePermissionToRoles('view-l-room');
|
||||
});
|
||||
it('should return an array of tags', async () => {
|
||||
const { tag } = tagsData.caseA;
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(credentials)
|
||||
.query({ text: tag.name, viewAll: 'true' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(1);
|
||||
expect(response.body.tags[0]).to.have.property('_id', tag._id);
|
||||
});
|
||||
it('[Manager role] show return all tags when "viewAll" param is true and user has access to all tags', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(credentials)
|
||||
.query({ viewAll: 'true' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(3);
|
||||
|
||||
const expectedTags = [tagsData.caseA.tag, tagsData.caseB.tag, tagsData.casePublic.tag].map((tag) => tag._id).sort();
|
||||
const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort();
|
||||
expect(actualTags).to.deep.equal(expectedTags);
|
||||
});
|
||||
it('[Monitor role] show return only public tags when "viewAll" param is true and user does not have access to all tags', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(monitor.credentials)
|
||||
.query({ viewAll: 'true' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(1);
|
||||
|
||||
const expectedTags = [tagsData.casePublic.tag].map((tag) => tag._id).sort();
|
||||
const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort();
|
||||
expect(actualTags).to.deep.equal(expectedTags);
|
||||
});
|
||||
it('[Manager Role] should return department tags and public tags when "departmentId" param is provided', async () => {
|
||||
const { department } = tagsData.caseA;
|
||||
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(credentials)
|
||||
.query({ department: department._id })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(2);
|
||||
|
||||
const expectedTags = [tagsData.caseA.tag, tagsData.casePublic.tag].map((tag) => tag._id).sort();
|
||||
const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort();
|
||||
expect(actualTags).to.deep.equal(expectedTags);
|
||||
});
|
||||
it('[Manager role] should return public tags when "departmentId" param is not provided', async () => {
|
||||
const response = await request.get(api('livechat/tags')).set(credentials).expect('Content-Type', 'application/json').expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(1);
|
||||
expect(response.body.tags[0]).to.have.property('_id', tagsData.casePublic.tag._id);
|
||||
});
|
||||
it('[Monitor role] should return only public tags when user does not have access to department', async () => {
|
||||
const { department } = tagsData.caseA;
|
||||
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(monitor.credentials)
|
||||
.query({ department: department._id })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(1);
|
||||
|
||||
const expectedTags = [tagsData.casePublic.tag].map((tag) => tag._id).sort();
|
||||
const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort();
|
||||
expect(actualTags).to.deep.equal(expectedTags);
|
||||
});
|
||||
it('[Monitor Role] should return department tags and public tags when "departmentId" param is provided', async () => {
|
||||
const { department } = tagsData.caseA;
|
||||
|
||||
await createUnit(monitor.user._id, monitor.user.username || '', [department._id]);
|
||||
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(monitor.credentials)
|
||||
.query({ department: department._id })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(2);
|
||||
|
||||
const expectedTags = [tagsData.caseA.tag, tagsData.casePublic.tag].map((tag) => tag._id).sort();
|
||||
const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort();
|
||||
expect(actualTags).to.deep.equal(expectedTags);
|
||||
});
|
||||
it('[Agent Role] should return department tags and public tags when "departmentId" param is provided', async () => {
|
||||
const { department, agent } = tagsData.caseA;
|
||||
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(agent.credentials)
|
||||
.query({ department: department._id })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(2);
|
||||
|
||||
const expectedTags = [tagsData.caseA.tag, tagsData.casePublic.tag].map((tag) => tag._id).sort();
|
||||
const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort();
|
||||
expect(actualTags).to.deep.equal(expectedTags);
|
||||
});
|
||||
it('[Monitor role] should return public tags and department tags which they are part of, when "departmentId" param is not provided', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(monitor.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(2);
|
||||
const expectedTags = [tagsData.caseA.tag, tagsData.casePublic.tag].map((tag) => tag._id).sort();
|
||||
const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort();
|
||||
expect(actualTags).to.deep.equal(expectedTags);
|
||||
});
|
||||
it('[Agent role] should return public tags when "departmentId" param is not provided', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/tags'))
|
||||
.set(monitor.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body.tags).to.be.an('array').with.lengthOf(2);
|
||||
const expectedTags = [tagsData.caseA.tag, tagsData.casePublic.tag].map((tag) => tag._id).sort();
|
||||
const actualTags = response.body.tags.map((tag: ILivechatTag) => tag._id).sort();
|
||||
expect(actualTags).to.deep.equal(expectedTags);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/tags/:tagId', () => {
|
||||
it('should throw unauthorized error when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('manage-livechat-tags', []);
|
||||
await updatePermission('view-l-room', []);
|
||||
const response = await request.get(api('livechat/tags/123')).set(credentials).expect('Content-Type', 'application/json').expect(403);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return null when the tag does not exist', async () => {
|
||||
await updatePermission('manage-livechat-tags', ['admin']);
|
||||
await updatePermission('view-l-room', ['livechat-agent']);
|
||||
await request.get(api('livechat/tags/123')).set(credentials).expect('Content-Type', 'application/json').expect(404);
|
||||
});
|
||||
it('should return a tag', async () => {
|
||||
const tag = await saveTags();
|
||||
const response = await request
|
||||
.get(api(`livechat/tags/${tag._id}`))
|
||||
.set(credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(response.body).to.have.property('success', true);
|
||||
expect(response.body).to.have.property('_id', tag._id);
|
||||
expect(response.body).to.have.property('name', tag.name);
|
||||
expect(response.body).to.have.property('numDepartments', 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
739
apps/meteor/tests/end-to-end/api/livechat/14-units.ts
Normal file
739
apps/meteor/tests/end-to-end/api/livechat/14-units.ts
Normal file
@@ -0,0 +1,739 @@
|
||||
import type { ILivechatDepartment, IOmnichannelBusinessUnit } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { before, after, describe, it } from 'mocha';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { deleteDepartment, getDepartmentById } from '../../../data/livechat/department';
|
||||
import { createDepartment, updateDepartment } from '../../../data/livechat/rooms';
|
||||
import { createMonitor, createUnit, deleteUnit, getUnit } from '../../../data/livechat/units';
|
||||
import { updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import { password } from '../../../data/user';
|
||||
import { createUser, deleteUser, login } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
(IS_EE ? describe : describe.skip)('[EE] LIVECHAT - Units', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
await updatePermission('manage-livechat-departments', ['livechat-manager', 'livechat-monitor', 'admin']);
|
||||
await updateSetting('Omnichannel_enable_department_removal', true);
|
||||
});
|
||||
after(async () => {
|
||||
await updateSetting('Omnichannel_enable_department_removal', false);
|
||||
});
|
||||
|
||||
describe('[GET] livechat/units', () => {
|
||||
it('should return empty if manage-livechat-units permission is missing', async () => {
|
||||
await updatePermission('manage-livechat-units', []);
|
||||
return request
|
||||
.get(api('livechat/units'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
unitData: {
|
||||
name: 'test',
|
||||
enabled: true,
|
||||
},
|
||||
unitMonitors: [],
|
||||
unitDepartments: [],
|
||||
})
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('units').that.is.an('array').with.lengthOf(0);
|
||||
});
|
||||
});
|
||||
it('should return a list of units', async () => {
|
||||
await updatePermission('manage-livechat-units', ['admin']);
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request.get(api('livechat/units')).set(credentials).expect(200);
|
||||
expect(body.units).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
const unitFound = body.units.find((u: IOmnichannelBusinessUnit) => u._id === unit._id);
|
||||
expect(unitFound).to.have.property('_id', unit._id);
|
||||
expect(unitFound).to.have.property('name', unit.name);
|
||||
expect(unitFound).to.have.property('numMonitors', 1);
|
||||
expect(unitFound).to.have.property('numDepartments', 1);
|
||||
expect(unitFound).to.have.property('type', 'u');
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
|
||||
it('should return a list of units matching the provided filter', async () => {
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request.get(api('livechat/units')).query({ text: unit.name }).set(credentials).expect(200);
|
||||
expect(body.units).to.be.an('array').with.lengthOf(1);
|
||||
const unitFound = body.units.find((u: IOmnichannelBusinessUnit) => u._id === unit._id);
|
||||
expect(unitFound).to.have.property('_id', unit._id);
|
||||
expect(unitFound).to.have.property('name', unit.name);
|
||||
expect(unitFound).to.have.property('numMonitors', 1);
|
||||
expect(unitFound).to.have.property('numDepartments', 1);
|
||||
expect(unitFound).to.have.property('type', 'u');
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
|
||||
it('should properly paginate the result set', async () => {
|
||||
const { body } = await request.get(api('livechat/units')).query({ count: 1 }).set(credentials).expect(200);
|
||||
expect(body).to.have.property('units').and.to.be.an('array').with.lengthOf(1);
|
||||
expect(body).to.have.property('count').and.to.be.equal(1);
|
||||
const unit = body.units[0];
|
||||
|
||||
const { body: body2 } = await request.get(api('livechat/units')).query({ count: 1, offset: 1 }).set(credentials).expect(200);
|
||||
expect(body2).to.have.property('units').and.to.be.an('array').with.lengthOf(1);
|
||||
const unit2 = body2.units[0];
|
||||
|
||||
expect(unit._id).to.not.be.equal(unit2._id);
|
||||
});
|
||||
|
||||
it('should sort the result set based on provided fields', async () => {
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id], 'A test 1234');
|
||||
const unit2 = await createUnit(user._id, user.username, [department._id], 'a test 1234');
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/units'))
|
||||
.query({ sort: JSON.stringify({ name: 1 }), text: 'test', count: 2 })
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
expect(body).to.have.property('units').and.to.be.an('array').with.lengthOf(2);
|
||||
expect(body.units[0]._id).to.be.equal(unit._id);
|
||||
expect(body.units[1]._id).to.be.equal(unit2._id);
|
||||
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[POST] livechat/units', () => {
|
||||
it('should fail if manage-livechat-units permission is missing', async () => {
|
||||
await updatePermission('manage-livechat-units', []);
|
||||
return request
|
||||
.post(api('livechat/units'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
unitData: {
|
||||
name: 'test',
|
||||
enabled: true,
|
||||
},
|
||||
unitMonitors: [],
|
||||
unitDepartments: [],
|
||||
})
|
||||
.expect(403);
|
||||
});
|
||||
it('should return a created unit', async () => {
|
||||
await updatePermission('manage-livechat-units', ['admin']);
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/units'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
unitData: { name: 'test', visibility: 'public', enabled: true, description: 'test' },
|
||||
unitMonitors: [{ monitorId: user._id, username: user.username }],
|
||||
unitDepartments: [{ departmentId: department._id }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('_id');
|
||||
expect(body).to.have.property('name', 'test');
|
||||
expect(body).to.have.property('visibility', 'public');
|
||||
expect(body).to.have.property('type', 'u');
|
||||
expect(body).to.have.property('numMonitors', 1);
|
||||
expect(body).to.have.property('numDepartments', 1);
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
|
||||
it('should return a unit with no monitors if a user who is not a monitor is passed', async () => {
|
||||
await updatePermission('manage-livechat-units', ['admin']);
|
||||
const user = await createUser();
|
||||
const department = await createDepartment();
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/units'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
unitData: { name: 'test', visibility: 'public', enabled: true, description: 'test' },
|
||||
unitMonitors: [{ monitorId: user._id, username: user.username }],
|
||||
unitDepartments: [{ departmentId: department._id }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('numMonitors', 0);
|
||||
expect(body).to.have.property('name', 'test');
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[GET] livechat/units/:id', () => {
|
||||
it('should fail if manage-livechat-units permission is missing', async () => {
|
||||
await updatePermission('manage-livechat-units', []);
|
||||
return request.get(api('livechat/units/123')).set(credentials).send().expect(403);
|
||||
});
|
||||
it('should return a unit', async () => {
|
||||
await updatePermission('manage-livechat-units', ['admin']);
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.get(api(`livechat/units/${unit._id}`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
expect(body).to.have.property('_id', unit._id);
|
||||
expect(body).to.have.property('name', unit.name);
|
||||
expect(body).to.have.property('numMonitors', 1);
|
||||
expect(body).to.have.property('numDepartments', 1);
|
||||
expect(body).to.have.property('type', 'u');
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[POST] livechat/units/:id', () => {
|
||||
it('should fail if manage-livechat-units permission is missing', async () => {
|
||||
await updatePermission('manage-livechat-units', []);
|
||||
return request.post(api('livechat/units/123')).set(credentials).expect(403);
|
||||
});
|
||||
it('should fail if unit does not exist', async () => {
|
||||
await updatePermission('manage-livechat-units', ['admin']);
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/units/123'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
unitData: { name: 'test', visibility: 'public', enabled: true, description: 'test' },
|
||||
unitMonitors: [{ monitorId: user._id, username: user.username }],
|
||||
unitDepartments: [{ departmentId: department._id }],
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(body).to.have.property('success', false);
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
it('should return a updated unit', async () => {
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.post(api(`livechat/units/${unit._id}`))
|
||||
.set(credentials)
|
||||
.send({
|
||||
unitData: { name: 'test', visibility: 'private', enabled: true, description: 'test' },
|
||||
unitMonitors: [{ monitorId: user._id, username: user.username }],
|
||||
unitDepartments: [{ departmentId: department._id }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('_id');
|
||||
expect(body).to.have.property('name', 'test');
|
||||
expect(body).to.have.property('visibility', 'private');
|
||||
expect(body).to.have.property('type', 'u');
|
||||
expect(body).to.have.property('numMonitors', 1);
|
||||
expect(body).to.have.property('numDepartments', 1);
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
it('should move the department to the latest unit that attempted to assign it', async () => {
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit1 = await createUnit(user._id, user.username, [department._id]);
|
||||
const unit2 = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.get(api(`livechat/units/${unit1._id}/departments`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('departments');
|
||||
expect(body.departments).to.have.lengthOf(0);
|
||||
expect(unit2.numDepartments).to.be.equal(1);
|
||||
});
|
||||
it('should remove the department from the unit if it is not passed in the request', async () => {
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit1 = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.post(api(`livechat/units/${unit1._id}`))
|
||||
.set(credentials)
|
||||
.send({
|
||||
unitData: { name: unit1.name, visibility: unit1.visibility },
|
||||
unitMonitors: [{ monitorId: user._id, username: user.username }],
|
||||
unitDepartments: [],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('_id', unit1._id);
|
||||
expect(body).to.have.property('numDepartments', 0);
|
||||
});
|
||||
it('should remove the monitor from the unit if it is not passed in the request', async () => {
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit1 = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.post(api(`livechat/units/${unit1._id}`))
|
||||
.set(credentials)
|
||||
.send({
|
||||
unitData: { name: unit1.name, visibility: unit1.visibility },
|
||||
unitMonitors: [],
|
||||
unitDepartments: [{ departmentId: department._id }],
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('_id', unit1._id);
|
||||
expect(body).to.have.property('numMonitors', 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[DELETE] livechat/units/:id', () => {
|
||||
it('should fail if manage-livechat-units permission is missing', async () => {
|
||||
await updatePermission('manage-livechat-units', []);
|
||||
return request.delete(api('livechat/units/123')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return a deleted unit', async () => {
|
||||
await updatePermission('manage-livechat-units', ['admin']);
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.delete(api(`livechat/units/${unit._id}`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.be.a('number').equal(1);
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/units/:unitId/departments', () => {
|
||||
it('should fail if manage-livechat-units permission is missing', async () => {
|
||||
await updatePermission('manage-livechat-units', []);
|
||||
return request.get(api('livechat/units/123/departments')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return departments associated with a unit', async () => {
|
||||
await updatePermission('manage-livechat-units', ['admin']);
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.get(api(`livechat/units/${unit._id}/departments`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('departments');
|
||||
expect(body.departments).to.have.lengthOf(1);
|
||||
expect(body.departments[0]).to.have.property('_id', department._id);
|
||||
expect(body.departments[0]).to.have.property('name', department.name);
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/units/:unitId/departments/available', () => {
|
||||
it('should fail if manage-livechat-units permission is missing', async () => {
|
||||
await updatePermission('manage-livechat-units', []);
|
||||
return request.get(api('livechat/units/123/departments/available')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return departments not associated with a unit', async () => {
|
||||
await updatePermission('manage-livechat-units', ['admin']);
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.get(api(`livechat/units/${unit._id}/departments/available`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('departments');
|
||||
expect(body.departments).to.have.lengthOf.greaterThan(0);
|
||||
|
||||
const myUnit = body.departments.find((d: ILivechatDepartment) => d.parentId === unit._id);
|
||||
expect(myUnit).to.not.be.undefined.and.not.be.null;
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/units/:unitId/monitors', () => {
|
||||
it('should fail if manage-livechat-units permission is missing', async () => {
|
||||
await updatePermission('manage-livechat-monitors', []);
|
||||
return request.get(api('livechat/units/123/monitors')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return monitors associated with a unit', async () => {
|
||||
await updatePermission('manage-livechat-monitors', ['admin']);
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
const department = await createDepartment();
|
||||
const unit = await createUnit(user._id, user.username, [department._id]);
|
||||
|
||||
const { body } = await request
|
||||
.get(api(`livechat/units/${unit._id}/monitors`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('monitors');
|
||||
expect(body.monitors).to.have.lengthOf(1);
|
||||
expect(body.monitors[0]).to.have.property('monitorId', user._id);
|
||||
expect(body.monitors[0]).to.have.property('username', user.username);
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[POST] livechat/department', () => {
|
||||
let monitor1: Awaited<ReturnType<typeof createUser>>;
|
||||
let monitor1Credentials: Awaited<ReturnType<typeof login>>;
|
||||
let monitor2: Awaited<ReturnType<typeof createUser>>;
|
||||
let monitor2Credentials: Awaited<ReturnType<typeof login>>;
|
||||
let unit: IOmnichannelBusinessUnit;
|
||||
|
||||
before(async () => {
|
||||
monitor1 = await createUser();
|
||||
monitor2 = await createUser();
|
||||
await createMonitor(monitor1.username);
|
||||
monitor1Credentials = await login(monitor1.username, password);
|
||||
await createMonitor(monitor2.username);
|
||||
monitor2Credentials = await login(monitor2.username, password);
|
||||
unit = await createUnit(monitor1._id, monitor1.username, []);
|
||||
});
|
||||
|
||||
after(async () => Promise.all([deleteUser(monitor1), deleteUser(monitor2), deleteUnit(unit)]));
|
||||
|
||||
it('should fail creating department when providing an invalid property in the department unit object', () => {
|
||||
return request
|
||||
.post(api('livechat/department'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
department: { name: 'Fail-Test', enabled: true, showOnOfflineForm: true, showOnRegistration: true, email: 'bla@bla' },
|
||||
departmentUnit: { invalidProperty: true },
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('errorType', 'invalid-params');
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail creating a department into an existing unit that a monitor does not supervise', async () => {
|
||||
const department = await createDepartment(undefined, undefined, undefined, undefined, { _id: unit._id }, monitor2Credentials);
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 0);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.not.have.property('parentId');
|
||||
expect(fullDepartment).to.not.have.property('ancestors');
|
||||
|
||||
await deleteDepartment(department._id);
|
||||
});
|
||||
|
||||
it('should succesfully create a department into an existing unit as an admin', async () => {
|
||||
const department = await createDepartment(undefined, undefined, undefined, undefined, { _id: unit._id });
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 1);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.have.property('parentId', unit._id);
|
||||
expect(fullDepartment).to.have.property('ancestors').that.is.an('array').with.lengthOf(1);
|
||||
expect(fullDepartment.ancestors?.[0]).to.equal(unit._id);
|
||||
|
||||
await deleteDepartment(department._id);
|
||||
});
|
||||
|
||||
it('should succesfully create a department into an existing unit that a monitor supervises', async () => {
|
||||
const department = await createDepartment(undefined, undefined, undefined, undefined, { _id: unit._id }, monitor1Credentials);
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 1);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.have.property('parentId', unit._id);
|
||||
expect(fullDepartment).to.have.property('ancestors').that.is.an('array').with.lengthOf(1);
|
||||
expect(fullDepartment.ancestors?.[0]).to.equal(unit._id);
|
||||
|
||||
await deleteDepartment(department._id);
|
||||
});
|
||||
|
||||
it('unit should end up with 0 departments after removing all of them', async () => {
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[PUT] livechat/department', () => {
|
||||
let monitor1: Awaited<ReturnType<typeof createUser>>;
|
||||
let monitor1Credentials: Awaited<ReturnType<typeof login>>;
|
||||
let monitor2: Awaited<ReturnType<typeof createUser>>;
|
||||
let monitor2Credentials: Awaited<ReturnType<typeof login>>;
|
||||
let unit: IOmnichannelBusinessUnit;
|
||||
let department: ILivechatDepartment;
|
||||
let baseDepartment: ILivechatDepartment;
|
||||
|
||||
before(async () => {
|
||||
monitor1 = await createUser();
|
||||
monitor2 = await createUser();
|
||||
await createMonitor(monitor1.username);
|
||||
monitor1Credentials = await login(monitor1.username, password);
|
||||
await createMonitor(monitor2.username);
|
||||
monitor2Credentials = await login(monitor2.username, password);
|
||||
department = await createDepartment();
|
||||
baseDepartment = await createDepartment();
|
||||
unit = await createUnit(monitor1._id, monitor1.username, [baseDepartment._id]);
|
||||
});
|
||||
|
||||
after(async () =>
|
||||
Promise.all([
|
||||
deleteUser(monitor1),
|
||||
deleteUser(monitor2),
|
||||
deleteUnit(unit),
|
||||
deleteDepartment(department._id),
|
||||
deleteDepartment(baseDepartment._id),
|
||||
]),
|
||||
);
|
||||
|
||||
it("should fail updating a department's unit when providing an invalid property in the department unit object", () => {
|
||||
const updatedName = 'updated-department-name';
|
||||
return request
|
||||
.put(api(`livechat/department/${department._id}`))
|
||||
.set(credentials)
|
||||
.send({
|
||||
department: { name: updatedName, enabled: true, showOnOfflineForm: true, showOnRegistration: true, email: 'bla@bla' },
|
||||
departmentUnit: { invalidProperty: true },
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'Match error: Unknown key in field departmentUnit.invalidProperty');
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail updating a department's unit when providing an invalid _id type in the department unit object", () => {
|
||||
const updatedName = 'updated-department-name';
|
||||
return request
|
||||
.put(api(`livechat/department/${department._id}`))
|
||||
.set(credentials)
|
||||
.send({
|
||||
department: { name: updatedName, enabled: true, showOnOfflineForm: true, showOnRegistration: true, email: 'bla@bla' },
|
||||
departmentUnit: { _id: true },
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error', 'Match error: Expected string, got boolean in field departmentUnit._id');
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail removing the last department from a unit', () => {
|
||||
const updatedName = 'updated-department-name';
|
||||
return request
|
||||
.put(api(`livechat/department/${baseDepartment._id}`))
|
||||
.set(credentials)
|
||||
.send({
|
||||
department: { name: updatedName, enabled: true, showOnOfflineForm: true, showOnRegistration: true, email: 'bla@bla' },
|
||||
departmentUnit: {},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('errorType', 'error-unit-cant-be-empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('should succesfully add an existing department to a unit as an admin', async () => {
|
||||
const updatedName = 'updated-department-name';
|
||||
|
||||
const updatedDepartment = await updateDepartment({
|
||||
userCredentials: credentials,
|
||||
departmentId: department._id,
|
||||
name: updatedName,
|
||||
departmentUnit: { _id: unit._id },
|
||||
});
|
||||
expect(updatedDepartment).to.have.property('name', updatedName);
|
||||
expect(updatedDepartment).to.have.property('type', 'd');
|
||||
expect(updatedDepartment).to.have.property('_id', department._id);
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 2);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.have.property('parentId', unit._id);
|
||||
expect(fullDepartment).to.have.property('ancestors').that.is.an('array').with.lengthOf(1);
|
||||
expect(fullDepartment.ancestors?.[0]).to.equal(unit._id);
|
||||
});
|
||||
|
||||
it('should succesfully remove an existing department from a unit as an admin', async () => {
|
||||
const updatedName = 'updated-department-name';
|
||||
|
||||
const updatedDepartment = await updateDepartment({
|
||||
userCredentials: credentials,
|
||||
departmentId: department._id,
|
||||
name: updatedName,
|
||||
departmentUnit: {},
|
||||
});
|
||||
expect(updatedDepartment).to.have.property('name', updatedName);
|
||||
expect(updatedDepartment).to.have.property('type', 'd');
|
||||
expect(updatedDepartment).to.have.property('_id', department._id);
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 1);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.not.have.property('parentId');
|
||||
expect(fullDepartment).to.not.have.property('ancestors');
|
||||
});
|
||||
|
||||
it('should fail adding a department into an existing unit that a monitor does not supervise', async () => {
|
||||
const updatedName = 'updated-department-name2';
|
||||
|
||||
const updatedDepartment = await updateDepartment({
|
||||
userCredentials: monitor2Credentials,
|
||||
departmentId: department._id,
|
||||
name: updatedName,
|
||||
departmentUnit: { _id: unit._id },
|
||||
});
|
||||
expect(updatedDepartment).to.have.property('name', updatedName);
|
||||
expect(updatedDepartment).to.have.property('type', 'd');
|
||||
expect(updatedDepartment).to.have.property('_id', department._id);
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 1);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.not.have.property('parentId');
|
||||
expect(fullDepartment).to.not.have.property('ancestors');
|
||||
});
|
||||
|
||||
it('should succesfully add a department into an existing unit that a monitor supervises', async () => {
|
||||
const updatedName = 'updated-department-name3';
|
||||
|
||||
const updatedDepartment = await updateDepartment({
|
||||
userCredentials: monitor1Credentials,
|
||||
departmentId: department._id,
|
||||
name: updatedName,
|
||||
departmentUnit: { _id: unit._id },
|
||||
});
|
||||
expect(updatedDepartment).to.have.property('name', updatedName);
|
||||
expect(updatedDepartment).to.have.property('type', 'd');
|
||||
expect(updatedDepartment).to.have.property('_id', department._id);
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 2);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.have.property('name', updatedName);
|
||||
expect(fullDepartment).to.have.property('parentId', unit._id);
|
||||
expect(fullDepartment).to.have.property('ancestors').that.is.an('array').with.lengthOf(1);
|
||||
expect(fullDepartment.ancestors?.[0]).to.equal(unit._id);
|
||||
});
|
||||
|
||||
it('should fail removing a department from a unit that a monitor does not supervise', async () => {
|
||||
const updatedName = 'updated-department-name4';
|
||||
|
||||
const updatedDepartment = await updateDepartment({
|
||||
userCredentials: monitor2Credentials,
|
||||
departmentId: department._id,
|
||||
name: updatedName,
|
||||
departmentUnit: {},
|
||||
});
|
||||
expect(updatedDepartment).to.have.property('name', updatedName);
|
||||
expect(updatedDepartment).to.have.property('type', 'd');
|
||||
expect(updatedDepartment).to.have.property('_id', department._id);
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 2);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.have.property('name', updatedName);
|
||||
expect(fullDepartment).to.have.property('parentId', unit._id);
|
||||
expect(fullDepartment).to.have.property('ancestors').that.is.an('array').with.lengthOf(1);
|
||||
expect(fullDepartment.ancestors?.[0]).to.equal(unit._id);
|
||||
});
|
||||
|
||||
it('should succesfully remove a department from a unit that a monitor supervises', async () => {
|
||||
const updatedName = 'updated-department-name5';
|
||||
|
||||
const updatedDepartment = await updateDepartment({
|
||||
userCredentials: monitor1Credentials,
|
||||
departmentId: department._id,
|
||||
name: updatedName,
|
||||
departmentUnit: {},
|
||||
});
|
||||
expect(updatedDepartment).to.have.property('name', updatedName);
|
||||
expect(updatedDepartment).to.have.property('type', 'd');
|
||||
expect(updatedDepartment).to.have.property('_id', department._id);
|
||||
|
||||
const updatedUnit = await getUnit(unit._id);
|
||||
expect(updatedUnit).to.have.property('name', unit.name);
|
||||
expect(updatedUnit).to.have.property('numMonitors', 1);
|
||||
expect(updatedUnit).to.have.property('numDepartments', 1);
|
||||
|
||||
const fullDepartment = await getDepartmentById(department._id);
|
||||
expect(fullDepartment).to.have.property('name', updatedName);
|
||||
expect(fullDepartment).to.not.have.property('parentId');
|
||||
expect(fullDepartment).to.not.have.property('ancestors');
|
||||
});
|
||||
});
|
||||
});
|
||||
296
apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts
Normal file
296
apps/meteor/tests/end-to-end/api/livechat/15-canned-responses.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import type { IOmnichannelCannedResponse } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { before, describe, it } from 'mocha';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { createCannedResponse } from '../../../data/livechat/canned-responses';
|
||||
import { createAgent, createDepartment } from '../../../data/livechat/rooms';
|
||||
import { removeTag, saveTags } from '../../../data/livechat/tags';
|
||||
import { createMonitor, createUnit } from '../../../data/livechat/units';
|
||||
import { updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import { password, adminUsername } from '../../../data/user';
|
||||
import { createUser, login } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
(IS_EE ? describe : describe.skip)('[EE] LIVECHAT - Canned responses', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
});
|
||||
|
||||
describe('canned-responses.get', () => {
|
||||
it('should throw unauthorized when view-canned-responses permission is not set', async () => {
|
||||
await updatePermission('view-canned-responses', []);
|
||||
return request.get(api('canned-responses.get')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return an array of canned responses when available', async () => {
|
||||
await updatePermission('view-canned-responses', ['livechat-agent', 'livechat-monitor', 'livechat-manager', 'admin']);
|
||||
await createCannedResponse();
|
||||
const { body } = await request.get(api('canned-responses.get')).set(credentials).expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.responses).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.responses[0]).to.have.property('_id');
|
||||
expect(body.responses[0]).to.have.property('shortcut');
|
||||
expect(body.responses[0]).to.have.property('scope');
|
||||
expect(body.responses[0]).to.have.property('tags');
|
||||
expect(body.responses[0]).to.have.property('text');
|
||||
expect(body.responses[0]).to.have.property('userId');
|
||||
});
|
||||
it('should return canned responses for monitors even if department is disabled', async () => {
|
||||
const user = await createUser();
|
||||
const monitor = await createMonitor(user.username);
|
||||
const creds = await login(user.username, password);
|
||||
const department = await createDepartment(undefined, undefined, false);
|
||||
await createUnit(monitor._id, user.username, [department._id]);
|
||||
|
||||
// create canned response
|
||||
const shortcut = faker.string.uuid();
|
||||
await request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ shortcut, scope: 'department', tags: ['tag'], text: 'text', departmentId: department._id })
|
||||
.expect(200);
|
||||
|
||||
const { body } = await request.get(api('canned-responses.get')).set(creds).expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.responses).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.responses.find((response: IOmnichannelCannedResponse) => response.shortcut === shortcut)).to.be.an('object');
|
||||
});
|
||||
it('should return canned respones for scope user when agent is not on any department', async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const creds = await login(user.username, password);
|
||||
|
||||
// create canned response
|
||||
const shortcut = faker.string.uuid();
|
||||
await request
|
||||
.post(api('canned-responses'))
|
||||
.set(creds)
|
||||
.send({ shortcut, scope: 'user', tags: ['tag'], text: 'text' })
|
||||
.expect(200);
|
||||
|
||||
const { body } = await request.get(api('canned-responses.get')).set(creds).expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.responses).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.responses.find((response: IOmnichannelCannedResponse) => response.shortcut === shortcut)).to.be.an('object');
|
||||
});
|
||||
it('should return canned responses on the global scope', async () => {
|
||||
const user = await createUser();
|
||||
const creds = await login(user.username, password);
|
||||
await createAgent(user.username);
|
||||
|
||||
// create canned response
|
||||
const shortcut = faker.string.uuid();
|
||||
await request.post(api('canned-responses')).set(credentials).send({ shortcut, scope: 'global', text: 'text' }).expect(200);
|
||||
|
||||
const { body } = await request.get(api('canned-responses.get')).set(creds).expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.responses).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.responses.find((response: IOmnichannelCannedResponse) => response.shortcut === shortcut)).to.be.an('object');
|
||||
});
|
||||
it('should return canned responses from the departments user is in', async () => {
|
||||
const user = await createUser();
|
||||
const creds = await login(user.username, password);
|
||||
await createAgent(user.username);
|
||||
const department = await createDepartment([{ agentId: user._id }], undefined, true);
|
||||
|
||||
// create canned response
|
||||
const shortcut = faker.string.uuid();
|
||||
await request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ shortcut, scope: 'department', text: 'text', departmentId: department._id })
|
||||
.expect(200);
|
||||
|
||||
const { body } = await request.get(api('canned-responses.get')).set(creds).expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.responses).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.responses.find((response: IOmnichannelCannedResponse) => response.shortcut === shortcut)).to.be.an('object');
|
||||
});
|
||||
});
|
||||
|
||||
describe('[GET] canned-responses', () => {
|
||||
it('should fail if user dont have view-canned-responses permission', async () => {
|
||||
await updatePermission('view-canned-responses', []);
|
||||
return request.get(api('canned-responses')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return an array of canned responses when available', async () => {
|
||||
await updatePermission('view-canned-responses', ['livechat-agent', 'livechat-monitor', 'livechat-manager', 'admin']);
|
||||
await createCannedResponse();
|
||||
const { body } = await request.get(api('canned-responses')).query({ sort: '{ "_createdAt": -1 }' }).set(credentials).expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.cannedResponses).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.cannedResponses[0]).to.have.property('_id');
|
||||
expect(body.cannedResponses[0]).to.have.property('shortcut');
|
||||
expect(body.cannedResponses[0]).to.have.property('scope');
|
||||
expect(body.cannedResponses[0]).to.have.property('tags');
|
||||
expect(body.cannedResponses[0]).to.have.property('text');
|
||||
expect(body.cannedResponses[0]).to.have.property('userId');
|
||||
});
|
||||
it('should return a canned response matching the params provided (shortcut)', async () => {
|
||||
const response = await createCannedResponse();
|
||||
const { body } = await request.get(api('canned-responses')).set(credentials).query({ shortcut: response.shortcut }).expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.cannedResponses).to.be.an('array').with.lengthOf(1);
|
||||
expect(body.cannedResponses[0]).to.have.property('_id');
|
||||
expect(body.cannedResponses[0]).to.have.property('shortcut', response.shortcut);
|
||||
});
|
||||
it('should return a canned response matching the params provided (scope)', async () => {
|
||||
const response = await createCannedResponse();
|
||||
const { body } = await request.get(api('canned-responses')).set(credentials).query({ scope: response.scope }).expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.cannedResponses).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.cannedResponses[0]).to.have.property('_id');
|
||||
expect(body.cannedResponses[0]).to.have.property('scope', response.scope);
|
||||
});
|
||||
it('should return a canned response matching the params provided (tags)', async () => {
|
||||
const response = await createCannedResponse();
|
||||
const { body } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': response.tags?.[0] }).expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.cannedResponses).to.be.an('array').with.lengthOf(1);
|
||||
expect(body.cannedResponses[0]).to.have.property('_id');
|
||||
expect(body.cannedResponses[0]).to.have.property('tags').that.is.an('array').which.includes(response.tags?.[0]);
|
||||
});
|
||||
it('should return a canned response matching the params provided (text)', async () => {
|
||||
const response = await createCannedResponse();
|
||||
const { body } = await request.get(api('canned-responses')).set(credentials).query({ text: response.text }).expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.cannedResponses).to.be.an('array').with.lengthOf(1);
|
||||
expect(body.cannedResponses[0]).to.have.property('_id');
|
||||
expect(body.cannedResponses[0]).to.have.property('text', response.text);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[POST] canned-responses', () => {
|
||||
const dupshortcut = `shortcut-${faker.string.nanoid(6)}`;
|
||||
it('should fail if user dont have save-canned-responses permission', async () => {
|
||||
await updatePermission('save-canned-responses', []);
|
||||
return request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ shortcut: 'shortcut', scope: 'user', tags: ['tag'], text: 'text' })
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if shortcut is not on the request', async () => {
|
||||
await updatePermission('save-canned-responses', ['livechat-agent', 'livechat-monitor', 'livechat-manager', 'admin']);
|
||||
return request.post(api('canned-responses')).set(credentials).expect(400);
|
||||
});
|
||||
it('should fail if text is not on the request', async () => {
|
||||
return request.post(api('canned-responses')).set(credentials).send({ shortcut: 'shortcut' }).expect(400);
|
||||
});
|
||||
it('should fail if scope is not on the request', async () => {
|
||||
return request.post(api('canned-responses')).set(credentials).send({ shortcut: 'shortcut', text: 'text' }).expect(400);
|
||||
});
|
||||
it('should fail if tags is not an array of strings', async () => {
|
||||
return request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ shortcut: 'shortcut', text: 'text', scope: 'department', tags: 'tag' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should create a new canned response', async () => {
|
||||
const { body } = await request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ shortcut: dupshortcut, scope: 'user', tags: ['tag'], text: 'text' })
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
});
|
||||
it('should fail if shortcut is already in use', async () => {
|
||||
return request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ shortcut: dupshortcut, scope: 'user', tags: ['tag'], text: 'text' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should save a canned response related to an EE tag', async () => {
|
||||
const tag = await saveTags();
|
||||
|
||||
const { body } = await request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ shortcut: `eetag-${faker.string.nanoid(6)}`, scope: 'user', tags: [tag.name], text: 'text' })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
|
||||
const { body: getResult } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': tag.name }).expect(200);
|
||||
|
||||
expect(getResult).to.have.property('success', true);
|
||||
expect(getResult.cannedResponses).to.be.an('array').with.lengthOf(1);
|
||||
expect(getResult.cannedResponses[0]).to.have.property('_id');
|
||||
expect(getResult.cannedResponses[0]).to.have.property('tags').that.is.an('array').which.includes(tag.name);
|
||||
});
|
||||
it('should not include removed tags in the response', async () => {
|
||||
const tag = await saveTags();
|
||||
|
||||
const { body } = await request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ shortcut: `remove-${faker.string.nanoid(6)}`, scope: 'user', tags: [tag.name], text: 'text' })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
|
||||
await removeTag(tag._id);
|
||||
|
||||
const { body: getResult } = await request.get(api('canned-responses')).set(credentials).query({ 'tags[]': tag.name }).expect(200);
|
||||
|
||||
expect(getResult).to.have.property('success', true);
|
||||
expect(getResult.cannedResponses).to.be.an('array').with.lengthOf(0);
|
||||
});
|
||||
|
||||
it('should persist userId when updating a canned response', async () => {
|
||||
const response = await createCannedResponse();
|
||||
|
||||
const {
|
||||
body: { cannedResponses },
|
||||
} = await request.get(api('canned-responses')).set(credentials).query({}).expect(200);
|
||||
|
||||
const targetCannedResponse = cannedResponses.find(
|
||||
(cannedResponse: IOmnichannelCannedResponse) => cannedResponse.shortcut === response.shortcut,
|
||||
);
|
||||
|
||||
const { body } = await request
|
||||
.post(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.send({ _id: targetCannedResponse._id, shortcut: `${targetCannedResponse.shortcut}-edited`, text: 'edited text', scope: 'user' })
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
|
||||
const { body: getResult } = await request
|
||||
.get(api('canned-responses'))
|
||||
.set(credentials)
|
||||
.query({ shortcut: `${targetCannedResponse.shortcut}-edited` })
|
||||
.expect(200);
|
||||
expect(getResult).to.have.property('success', true);
|
||||
expect(getResult.cannedResponses[0]).to.have.property('userId', adminUsername);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[DELETE] canned-responses', () => {
|
||||
it('should fail if user dont have remove-canned-responses permission', async () => {
|
||||
await updatePermission('remove-canned-responses', []);
|
||||
return request.delete(api('canned-responses/sfdads')).set(credentials).expect(403);
|
||||
});
|
||||
it('should fail if _id is not on the request', async () => {
|
||||
await updatePermission('remove-canned-responses', ['livechat-agent', 'livechat-monitor', 'livechat-manager', 'admin']);
|
||||
return request.delete(api('canned-responses')).set(credentials).expect(400);
|
||||
});
|
||||
it('should delete a canned response', async () => {
|
||||
const response = await createCannedResponse();
|
||||
const { body: cr } = await request.get(api('canned-responses')).set(credentials).query({ shortcut: response.shortcut }).expect(200);
|
||||
const { body } = await request
|
||||
.delete(api(`canned-responses/${cr.cannedResponses[0]._id}`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
821
apps/meteor/tests/end-to-end/api/livechat/17-dashboards-ee.ts
Normal file
821
apps/meteor/tests/end-to-end/api/livechat/17-dashboards-ee.ts
Normal file
@@ -0,0 +1,821 @@
|
||||
import type { ILivechatDepartment, ILivechatVisitor } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { before, describe, it, after } from 'mocha';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { createDepartment, deleteDepartment } from '../../../data/livechat/department';
|
||||
import {
|
||||
createVisitor,
|
||||
createLivechatRoom,
|
||||
takeInquiry,
|
||||
closeOmnichannelRoom,
|
||||
makeAgentUnavailable,
|
||||
makeAgentAvailable,
|
||||
createAgent,
|
||||
sendAgentMessage,
|
||||
sendMessage,
|
||||
fetchInquiry,
|
||||
deleteVisitor,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import { updateEESetting, updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import { pagination } from '../../../data/utils';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
(IS_EE ? describe : describe.skip)('[EE] LIVECHAT - dashboards', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
await updateEESetting('Livechat_Require_Contact_Verification', 'never');
|
||||
await updateSetting('Omnichannel_enable_department_removal', true);
|
||||
await createAgent();
|
||||
await makeAgentAvailable();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Omnichannel_enable_department_removal', false);
|
||||
});
|
||||
|
||||
describe('livechat/analytics/agents/average-service-time', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/agents/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of agents', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const inq = await fetchInquiry(room._id);
|
||||
await takeInquiry(inq._id);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/agents/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.agents).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.agents[0]).to.have.a.property('_id');
|
||||
expect(body.agents[0]).to.have.a.property('username');
|
||||
expect(body.agents[0]).to.have.a.property('averageServiceTimeInSeconds').that.is.a('number');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/agents/average-service-time', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/agents/total-service-time', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/agents/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of agents', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/agents/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.agents).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.agents[0]).to.have.a.property('_id');
|
||||
expect(body.agents[0]).to.have.a.property('username');
|
||||
expect(body.agents[0]).to.have.a.property('serviceTimeDuration').that.is.a('number');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/agents/total-service-time', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/agents/available-for-service-history', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/agents/available-for-service-history'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/available-for-service-history'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/available-for-service-history'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/available-for-service-history'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/agents/available-for-service-history'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of agents', async () => {
|
||||
// Toggling to populate agent activity collection
|
||||
await createAgent();
|
||||
await makeAgentUnavailable();
|
||||
await makeAgentAvailable();
|
||||
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/agents/available-for-service-history'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.agents).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.agents[0]).to.have.a.property('username');
|
||||
expect(body.agents[0]).to.have.a.property('availableTimeInSeconds').that.is.a('number');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/agents/available-for-service-history', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/departments/amount-of-chats', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/departments/amount-of-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/amount-of-chats'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/amount-of-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/amount-of-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/amount-of-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of departments', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/departments/amount-of-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.departments).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.departments[0]).to.have.a.property('rooms').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('_id');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/departments/amount-of-chats', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/departments/average-service-time', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/departments/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
describe('with data', () => {
|
||||
let department: ILivechatDepartment;
|
||||
let visitor: ILivechatVisitor;
|
||||
|
||||
before(async () => {
|
||||
department = await createDepartment({}, [{ agentId: credentials['X-User-Id'] }]);
|
||||
|
||||
visitor = await createVisitor(department._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const inq = await fetchInquiry(room._id);
|
||||
await takeInquiry(inq._id);
|
||||
await sendAgentMessage(room._id);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteVisitor(visitor.token);
|
||||
await deleteDepartment(department._id);
|
||||
});
|
||||
|
||||
it('should return an array of departments', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/departments/average-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.departments).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.departments[0]).to.have.a.property('averageServiceTimeInSeconds').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('_id');
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/departments/average-service-time', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/departments/average-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 request
|
||||
.get(api('livechat/analytics/departments/average-chat-duration-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-chat-duration-time'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-chat-duration-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-chat-duration-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-chat-duration-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of departments', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/departments/average-chat-duration-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.departments).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.departments[0]).to.have.a.property('averageChatDurationTimeInSeconds').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('_id');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/departments/average-chat-duration-time', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/departments/total-service-time', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/departments/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of departments', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/departments/total-service-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.departments).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.departments[0]).to.have.a.property('chats').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('serviceTimeDuration').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('_id');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/departments/total-service-time', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/departments/average-waiting-time', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/departments/average-waiting-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-waiting-time'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-waiting-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-waiting-time'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/average-waiting-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
describe('with data', () => {
|
||||
let department: ILivechatDepartment;
|
||||
let visitor: ILivechatVisitor;
|
||||
|
||||
before(async () => {
|
||||
department = await createDepartment({}, [{ agentId: credentials['X-User-Id'] }]);
|
||||
visitor = await createVisitor(department._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const inq = await fetchInquiry(room._id);
|
||||
await takeInquiry(inq._id);
|
||||
await sendAgentMessage(room._id);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
});
|
||||
after(async () => {
|
||||
await deleteVisitor(visitor.token);
|
||||
await deleteDepartment(department._id);
|
||||
});
|
||||
|
||||
it('should return an array of departments', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/departments/average-waiting-time'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.departments).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.departments[0]).to.have.a.property('averageWaitingTimeInSeconds').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('_id');
|
||||
});
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/departments/average-waiting-time', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/departments/total-transferred-chats', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/departments/total-transferred-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-transferred-chats'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-transferred-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-transferred-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-transferred-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of departments', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/departments/total-transferred-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.departments).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.departments[0]).to.have.a.property('numberOfTransferredRooms').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('_id');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/departments/total-transferred-chats', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/departments/total-abandoned-chats', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/departments/total-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/total-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of departments', async () => {
|
||||
await updateSetting('Livechat_visitor_inactivity_timeout', 0);
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const inq = await fetchInquiry(room._id);
|
||||
await takeInquiry(inq._id);
|
||||
await sendMessage(room._id, 'first message', visitor.token);
|
||||
await sendAgentMessage(room._id);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/departments/total-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.departments).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.departments[0]).to.have.a.property('abandonedRooms').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('_id');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/departments/total-abandoned-chats', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/departments/percentage-abandoned-chats', () => {
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-manager', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/departments/percentage-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: '2020-01-02' })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
});
|
||||
it('should fail if start is not present as query param', async () => {
|
||||
await updatePermission('view-livechat-manager', ['admin']);
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/percentage-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ end: '2020-01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not present as query param', async () => {
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/percentage-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if start is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/percentage-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01x', end: date })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should fail if end is not a valid date', async () => {
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
const response = await request
|
||||
.get(api('livechat/analytics/departments/percentage-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: '2020-x01-02' })
|
||||
.expect(400);
|
||||
expect(response.body).to.have.property('success', false);
|
||||
});
|
||||
it('should return an array of departments', async () => {
|
||||
await updateSetting('Livechat_visitor_inactivity_timeout', 0);
|
||||
const date = new Date(new Date().setDate(new Date().getDate() - 2)).toISOString();
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/departments/percentage-abandoned-chats'))
|
||||
.set(credentials)
|
||||
.query({ start: date, end: new Date().toISOString() })
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
expect(body).to.have.property('success', true);
|
||||
expect(body.departments).to.be.an('array').with.lengthOf.greaterThan(0);
|
||||
expect(body.departments[0]).to.have.a.property('percentageOfAbandonedChats').that.is.a('number');
|
||||
expect(body.departments[0]).to.have.a.property('_id');
|
||||
});
|
||||
it('should accept pagination', async () => {
|
||||
await pagination('livechat/analytics/departments/percentage-abandoned-chats', credentials, {
|
||||
start: new Date().toISOString(),
|
||||
end: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
374
apps/meteor/tests/end-to-end/api/livechat/18-rooms-ee.ts
Normal file
374
apps/meteor/tests/end-to-end/api/livechat/18-rooms-ee.ts
Normal file
@@ -0,0 +1,374 @@
|
||||
import type { Credentials } from '@rocket.chat/api-client';
|
||||
import type { IOmnichannelRoom, IUser } 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 {
|
||||
createVisitor,
|
||||
createLivechatRoom,
|
||||
sendMessage,
|
||||
sendAgentMessage,
|
||||
placeRoomOnHold,
|
||||
getLivechatRoomInfo,
|
||||
startANewLivechatRoomAndTakeIt,
|
||||
makeAgentAvailable,
|
||||
createAgent,
|
||||
closeOmnichannelRoom,
|
||||
fetchMessages,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import { sleep } from '../../../data/livechat/utils';
|
||||
import { updateEESetting, updatePermission, updateSetting } from '../../../data/permissions.helper';
|
||||
import { password } from '../../../data/user';
|
||||
import { createUser, deleteUser, login } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
(IS_EE ? describe : describe.skip)('[EE] LIVECHAT - rooms', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
let agent2: { user: IUser; credentials: Credentials };
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
|
||||
await updateEESetting('Livechat_Require_Contact_Verification', 'never');
|
||||
await createAgent();
|
||||
await makeAgentAvailable();
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
const userCredentials = await login(user.username, password);
|
||||
await createAgent(user.username);
|
||||
await updateSetting('Livechat_allow_manual_on_hold', true);
|
||||
|
||||
agent2 = {
|
||||
user,
|
||||
credentials: userCredentials,
|
||||
};
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(agent2.user);
|
||||
await updateSetting('Livechat_allow_manual_on_hold', false);
|
||||
await updateSetting('Livechat_allow_manual_on_hold_upon_agent_engagement_only', true);
|
||||
});
|
||||
describe('livechat/room.onHold', () => {
|
||||
it('should fail if user doesnt have on-hold-livechat-room permission', async () => {
|
||||
await updatePermission('on-hold-livechat-room', []);
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: 'invalid-room-id',
|
||||
})
|
||||
.expect(403);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
|
||||
await updatePermission('on-hold-livechat-room', ['livechat-manager', 'livechat-monitor', 'livechat-agent', 'admin']);
|
||||
});
|
||||
it('should fail if roomId is invalid', async () => {
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: 'invalid-room-id',
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-invalid-room');
|
||||
});
|
||||
it('should fail if room is an empty string', async () => {
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: ' ',
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-invalid-room');
|
||||
});
|
||||
it('should fail if room is not a livechat room', async () => {
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: 'GENERAL',
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-invalid-room');
|
||||
});
|
||||
it('should fail if visitor is awaiting response (visitor sent last message)', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await sendMessage(room._id, 'test', visitor.token);
|
||||
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-cannot-place-chat-on-hold');
|
||||
});
|
||||
it('should fail if room is closed', async () => {
|
||||
const visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await closeOmnichannelRoom(room._id);
|
||||
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-room-already-closed');
|
||||
});
|
||||
it('should fail if user is not serving the chat and doesnt have on-hold-others-livechat-room permission', async () => {
|
||||
const { room } = await startANewLivechatRoomAndTakeIt();
|
||||
await sendAgentMessage(room._id);
|
||||
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(agent2.credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('Not_authorized');
|
||||
});
|
||||
it('should put room on hold', async () => {
|
||||
const { room } = await startANewLivechatRoomAndTakeIt();
|
||||
await sendAgentMessage(room._id);
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).to.be.true;
|
||||
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
expect(updatedRoom.onHold).to.be.true;
|
||||
});
|
||||
it('Should put room on hold, even in the visitor sent the last message', async () => {
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt();
|
||||
await updateSetting('Livechat_allow_manual_on_hold_upon_agent_engagement_only', false);
|
||||
await sendMessage(room._id, '-', visitor.token);
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(200);
|
||||
expect(response.body.success).to.be.true;
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
expect(updatedRoom.onHold).to.be.true;
|
||||
});
|
||||
it('should not put room on hold when visitor sent the last message', async () => {
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt();
|
||||
await updateSetting('Livechat_allow_manual_on_hold_upon_agent_engagement_only', true);
|
||||
await sendMessage(room._id, '-', visitor.token);
|
||||
const response = await request
|
||||
.post(api('livechat/room.onHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(400);
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-cannot-place-chat-on-hold');
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/room.resumeOnHold', () => {
|
||||
it('should fail if user doesnt have view-l-room permission', async () => {
|
||||
await updatePermission('view-l-room', []);
|
||||
const response = await request
|
||||
.post(api('livechat/room.resumeOnHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: 'invalid-room-id',
|
||||
})
|
||||
.expect(403);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
});
|
||||
it('should fail if roomId is invalid', async () => {
|
||||
await updatePermission('view-l-room', ['admin', 'livechat-manager', 'livechat-agent']);
|
||||
|
||||
const response = await request
|
||||
.post(api('livechat/room.resumeOnHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: 'invalid-room-id',
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-invalid-room');
|
||||
});
|
||||
it('should fail if room is not a livechat room', async () => {
|
||||
const response = await request
|
||||
.post(api('livechat/room.resumeOnHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: 'GENERAL',
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-invalid-room');
|
||||
});
|
||||
|
||||
it('should fail if room is not on hold', async () => {
|
||||
const { room } = await startANewLivechatRoomAndTakeIt();
|
||||
|
||||
const response = await request
|
||||
.post(api('livechat/room.resumeOnHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.success).to.be.false;
|
||||
expect(response.body.error).to.be.equal('error-room-not-on-hold');
|
||||
});
|
||||
it('should resume room on hold', async () => {
|
||||
const { room } = await startANewLivechatRoomAndTakeIt();
|
||||
|
||||
await sendAgentMessage(room._id);
|
||||
await placeRoomOnHold(room._id);
|
||||
|
||||
const response = await request
|
||||
.post(api('livechat/room.resumeOnHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).to.be.true;
|
||||
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
expect(updatedRoom).to.not.have.property('onHold');
|
||||
});
|
||||
it('should resume room on hold and send proper system message', async () => {
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt();
|
||||
|
||||
await sendAgentMessage(room._id);
|
||||
await placeRoomOnHold(room._id);
|
||||
|
||||
const response = await request
|
||||
.post(api('livechat/room.resumeOnHold'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(response.body.success).to.be.true;
|
||||
|
||||
const messages = await fetchMessages(room._id, visitor.token);
|
||||
expect(messages).to.be.an('array');
|
||||
expect(messages[0]).to.not.be.undefined;
|
||||
expect(messages[0]).to.have.property('t', 'omnichannel_on_hold_chat_resumed');
|
||||
expect(messages[0]).to.have.property('comment', 'The chat was manually resumed from On Hold by RocketChat Internal Admin Test');
|
||||
});
|
||||
it('should resume chat automatically if visitor sent a message', async () => {
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt();
|
||||
|
||||
await sendAgentMessage(room._id);
|
||||
await placeRoomOnHold(room._id);
|
||||
await sendMessage(room._id, 'test', visitor.token);
|
||||
|
||||
// wait for the room to be resumed since that logic is within callbacks
|
||||
await sleep(500);
|
||||
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
expect(updatedRoom).to.not.have.property('onHold');
|
||||
});
|
||||
});
|
||||
|
||||
describe('visitor abandonment feature', () => {
|
||||
let room: IOmnichannelRoom;
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_abandoned_rooms_action', 'Livechat_close_chat');
|
||||
await updateSetting('Livechat_visitor_inactivity_timeout', 60);
|
||||
});
|
||||
|
||||
it('should set predictedVisitorAbandonmentAt when agent sends a message', async () => {
|
||||
const { room: newRoom } = await startANewLivechatRoomAndTakeIt();
|
||||
|
||||
room = newRoom;
|
||||
|
||||
await sendAgentMessage(room._id);
|
||||
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
|
||||
const lastMessageTs = updatedRoom.responseBy?.lastMessageTs;
|
||||
const firstResponseTs = updatedRoom.responseBy?.firstResponseTs;
|
||||
const predictedVisitorAbandonmentAt = updatedRoom.omnichannel?.predictedVisitorAbandonmentAt;
|
||||
|
||||
expect(predictedVisitorAbandonmentAt).to.not.be.undefined;
|
||||
expect(lastMessageTs).to.not.be.undefined;
|
||||
expect(firstResponseTs).to.not.be.undefined;
|
||||
|
||||
// expect predictedVisitorAbandonmentAt to be 60 seconds after lastMessageTs
|
||||
const lastMessageTsDate = new Date(lastMessageTs as Date);
|
||||
const predictedVisitorAbandonmentAtDate = new Date(predictedVisitorAbandonmentAt as Date);
|
||||
const firstResponseTsDate = new Date(firstResponseTs as Date);
|
||||
|
||||
expect(predictedVisitorAbandonmentAtDate.getTime()).to.be.equal(lastMessageTsDate.getTime() + 60000);
|
||||
expect(firstResponseTsDate.getTime()).to.be.equal(lastMessageTsDate.getTime());
|
||||
});
|
||||
|
||||
it('should not update predictedVisitorAbandonmentAt when agent sends yet another message', async () => {
|
||||
await sendAgentMessage(room._id);
|
||||
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
|
||||
const lastMessageTs = updatedRoom.responseBy?.lastMessageTs;
|
||||
const firstResponseTs = updatedRoom.responseBy?.firstResponseTs;
|
||||
const predictedVisitorAbandonmentAt = updatedRoom.omnichannel?.predictedVisitorAbandonmentAt;
|
||||
|
||||
expect(predictedVisitorAbandonmentAt).to.not.be.undefined;
|
||||
expect(lastMessageTs).to.not.be.undefined;
|
||||
|
||||
// expect predictedVisitorAbandonmentAt to be 60 seconds after first message
|
||||
const lastMessageTsDate = new Date(lastMessageTs as Date);
|
||||
const predictedVisitorAbandonmentAtDate = new Date(predictedVisitorAbandonmentAt as Date);
|
||||
const firstResponseTsDate = new Date(firstResponseTs as Date);
|
||||
|
||||
expect(predictedVisitorAbandonmentAtDate.getTime()).to.be.equal(firstResponseTsDate.getTime() + 60000);
|
||||
|
||||
// lastMessageTs should be updated
|
||||
expect(lastMessageTsDate.getTime()).to.not.be.equal(firstResponseTsDate.getTime());
|
||||
expect(lastMessageTsDate.getTime()).to.be.greaterThan(firstResponseTsDate.getTime());
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_abandoned_rooms_action', 'none');
|
||||
await updateSetting('Livechat_visitor_inactivity_timeout', 3600);
|
||||
});
|
||||
});
|
||||
});
|
||||
1128
apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts
Normal file
1128
apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts
Normal file
File diff suppressed because it is too large
Load Diff
124
apps/meteor/tests/end-to-end/api/livechat/20-messages.ts
Normal file
124
apps/meteor/tests/end-to-end/api/livechat/20-messages.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import type { ILivechatAgent, ILivechatVisitor, IOmnichannelRoom, IRoom } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { before, describe, it, after } from 'mocha';
|
||||
|
||||
import { api, getCredentials, request } from '../../../data/api-data';
|
||||
import { sendSimpleMessage } from '../../../data/chat.helper';
|
||||
import {
|
||||
sendMessage,
|
||||
startANewLivechatRoomAndTakeIt,
|
||||
sendAgentMessage,
|
||||
createAgent,
|
||||
makeAgentAvailable,
|
||||
uploadFile,
|
||||
closeOmnichannelRoom,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import { removeAgent } from '../../../data/livechat/users';
|
||||
import { updateEESetting, updateSetting } from '../../../data/permissions.helper';
|
||||
import { createRoom, deleteRoom } from '../../../data/rooms.helper';
|
||||
|
||||
describe('LIVECHAT - messages', () => {
|
||||
let agent: ILivechatAgent;
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
agent = await createAgent();
|
||||
await makeAgentAvailable();
|
||||
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
|
||||
await updateEESetting('Livechat_Require_Contact_Verification', 'never');
|
||||
});
|
||||
|
||||
after(() => Promise.all([updateSetting('Livechat_Routing_Method', 'Auto_Selection'), removeAgent(agent._id)]));
|
||||
|
||||
describe('Quote message feature for visitors', () => {
|
||||
it('it should verify if visitor can quote message', async () => {
|
||||
const {
|
||||
room: { _id: roomId },
|
||||
visitor: { token },
|
||||
} = await startANewLivechatRoomAndTakeIt();
|
||||
|
||||
await sendMessage(roomId, 'Hello from visitor', token);
|
||||
const agentMsgSentence = faker.lorem.sentence();
|
||||
const agentMsg = await sendAgentMessage(roomId, agentMsgSentence);
|
||||
|
||||
const siteUrl = process.env.SITE_URL || process.env.TEST_API_URL || 'http://localhost:3000';
|
||||
|
||||
const msgLink = `${siteUrl}/live/${roomId}?msg=${agentMsg._id}`;
|
||||
const quotedMsgSentence = faker.lorem.sentence();
|
||||
const wholeQuotedMsg = `[${quotedMsgSentence}](${msgLink})`;
|
||||
|
||||
const quotedMessage = await sendMessage(roomId, wholeQuotedMsg, token);
|
||||
|
||||
expect(quotedMessage).to.have.property('_id');
|
||||
expect(quotedMessage).to.have.property('msg').that.is.equal(wholeQuotedMsg);
|
||||
|
||||
expect(quotedMessage).to.have.property('attachments');
|
||||
expect(quotedMessage.attachments).to.be.an('array').that.has.lengthOf(1);
|
||||
const quotedMessageAttachments = quotedMessage.attachments?.[0];
|
||||
if (quotedMessageAttachments) {
|
||||
expect(quotedMessageAttachments).to.have.property('message_link').that.is.equal(msgLink);
|
||||
expect(quotedMessageAttachments).to.have.property('text').that.is.equal(agentMsgSentence);
|
||||
}
|
||||
});
|
||||
|
||||
it('should verify if visitor is receiving a message with a image attachment', async () => {
|
||||
const {
|
||||
room: { _id: roomId },
|
||||
visitor: { token },
|
||||
} = await startANewLivechatRoomAndTakeIt();
|
||||
|
||||
const imgMessage = await uploadFile(roomId, token);
|
||||
|
||||
expect(imgMessage).to.have.property('files').that.is.an('array');
|
||||
expect(imgMessage.files?.[0]).to.have.keys('_id', 'name', 'type');
|
||||
expect(imgMessage).to.have.property('file').that.deep.equal(imgMessage?.files?.[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Livechat Messages', async () => {
|
||||
let room: IOmnichannelRoom;
|
||||
let privateRoom: IRoom;
|
||||
let visitor: ILivechatVisitor;
|
||||
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
|
||||
|
||||
const data = await startANewLivechatRoomAndTakeIt();
|
||||
visitor = data.visitor;
|
||||
room = data.room;
|
||||
|
||||
const response = await createRoom({ type: 'p', name: `private-room-${Math.random()}` } as any);
|
||||
privateRoom = response.body.group;
|
||||
});
|
||||
|
||||
after(() => Promise.all([closeOmnichannelRoom(room._id), deleteRoom({ roomId: privateRoom._id, type: 'p' })]));
|
||||
|
||||
it('should not allow fetching arbitrary messages from another channel', async () => {
|
||||
const response = await sendSimpleMessage({ roomId: privateRoom._id } as any);
|
||||
const { message } = response.body;
|
||||
|
||||
await request
|
||||
.get(api(`livechat/message/${message._id}`))
|
||||
.query({ token: visitor.token, rid: room._id })
|
||||
.send()
|
||||
.expect(400)
|
||||
.expect((res) => {
|
||||
expect(res.body.error).to.be.equal('invalid-message');
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow fetching messages using their _id and roomId', async () => {
|
||||
const message = await sendMessage(room._id, 'Hello from visitor', visitor.token);
|
||||
|
||||
await request
|
||||
.get(api(`livechat/message/${message._id}`))
|
||||
.query({ token: visitor.token, rid: room._id })
|
||||
.send()
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.message._id).to.be.equal(message._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
573
apps/meteor/tests/end-to-end/api/livechat/21-reports.ts
Normal file
573
apps/meteor/tests/end-to-end/api/livechat/21-reports.ts
Normal file
@@ -0,0 +1,573 @@
|
||||
import type { Credentials } from '@rocket.chat/api-client';
|
||||
import type { IUser, IOmnichannelRoom } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { after, before, describe, it } from 'mocha';
|
||||
|
||||
import { api, request, credentials, getCredentials } from '../../../data/api-data';
|
||||
import { createDepartment, addOrRemoveAgentFromDepartment } from '../../../data/livechat/department';
|
||||
import { startANewLivechatRoomAndTakeIt, createAgent } from '../../../data/livechat/rooms';
|
||||
import { createMonitor, createUnit } from '../../../data/livechat/units';
|
||||
import { restorePermissionToRoles, updateEESetting, updatePermission } from '../../../data/permissions.helper';
|
||||
import { password } from '../../../data/user';
|
||||
import { createUser, deleteUser, login } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
(IS_EE ? describe : describe.skip)('LIVECHAT - reports', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
let agent2: { user: IUser; credentials: Credentials };
|
||||
let agent3: { user: IUser; credentials: Credentials };
|
||||
|
||||
before(async () => {
|
||||
await updateEESetting('Livechat_Require_Contact_Verification', 'never');
|
||||
const user = await createUser();
|
||||
const userCredentials = await login(user.username, password);
|
||||
if (!user.username) {
|
||||
throw new Error('user not created');
|
||||
}
|
||||
await createMonitor(user.username);
|
||||
|
||||
agent2 = {
|
||||
user,
|
||||
credentials: userCredentials,
|
||||
};
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
const userCredentials = await login(user.username, password);
|
||||
await createAgent();
|
||||
if (!user.username) {
|
||||
throw new Error('user not created');
|
||||
}
|
||||
await createMonitor(user.username);
|
||||
const dep1 = await createDepartment();
|
||||
await addOrRemoveAgentFromDepartment(
|
||||
dep1._id,
|
||||
{ agentId: 'rocketchat.internal.admin.test', username: 'rocketchat.internal.admin.test', count: 0, order: 0 },
|
||||
true,
|
||||
);
|
||||
const { room, visitor } = await startANewLivechatRoomAndTakeIt({ departmentId: dep1._id });
|
||||
|
||||
await request
|
||||
.post(api('livechat/room.saveInfo'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
roomData: {
|
||||
_id: room._id,
|
||||
topic: 'new topic',
|
||||
tags: ['tag1', 'tag2'],
|
||||
},
|
||||
guestData: {
|
||||
_id: visitor._id,
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
});
|
||||
|
||||
await createUnit(user._id, user.username, [dep1._id]);
|
||||
|
||||
agent3 = {
|
||||
user,
|
||||
credentials: userCredentials,
|
||||
};
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(agent2.user);
|
||||
await deleteUser(agent3.user);
|
||||
});
|
||||
|
||||
describe('livechat/analytics/dashboards/conversations-by-source', () => {
|
||||
it('should return an error when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-reports', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(403);
|
||||
});
|
||||
it('should return an error when the start and end parameters are not provided', async () => {
|
||||
await restorePermissionToRoles('view-livechat-reports');
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-source')).set(credentials).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not provided', async () => {
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-source')).set(credentials).query({ end: 'test' }).expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not provided', async () => {
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-source')).set(credentials).query({ start: 'test' }).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should fail if dates are more than 1 year apart', async () => {
|
||||
const oneYearAgo = new Date(Date.now() - 380 * 24 * 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(credentials)
|
||||
.query({ start: oneYearAgo, end: now })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when start is after end', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: oneHourAgo })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return the proper data when the parameters are valid', async () => {
|
||||
// Note: this way all data will come as 0
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf(0);
|
||||
expect(body.total).to.be.equal(0);
|
||||
expect(body.success).to.be.true;
|
||||
});
|
||||
it('should return only public rooms for a monitor with no units', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(agent2.credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true;
|
||||
});
|
||||
it('should return only the data from the unit the monitor belongs to', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(agent3.credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf.greaterThan(0);
|
||||
expect(body.data.every((item: { value: number }) => item.value >= 0)).to.be.true;
|
||||
expect(body.total).to.be.greaterThan(0);
|
||||
expect(body.success).to.be.true;
|
||||
});
|
||||
it('should return valid data when login as a manager', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-source'))
|
||||
.set(credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf.greaterThan(0);
|
||||
expect(body.data.every((item: { value: number }) => item.value >= 0)).to.be.true;
|
||||
expect(body.total).to.be.greaterThan(0);
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/dashboards/conversations-by-status', () => {
|
||||
it('should return an error when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-reports', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-status'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(403);
|
||||
});
|
||||
it('should return an error when the start and end parameters are not provided', async () => {
|
||||
await restorePermissionToRoles('view-livechat-reports');
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-status')).set(credentials).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not provided', async () => {
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-status')).set(credentials).query({ end: 'test' }).expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not provided', async () => {
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-status')).set(credentials).query({ start: 'test' }).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-status'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-status'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error if dates are more than 1 year apart', async () => {
|
||||
const oneYearAgo = new Date(Date.now() - 380 * 24 * 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-status'))
|
||||
.set(credentials)
|
||||
.query({ start: oneYearAgo, end: now })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when start is after end', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-status'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: oneHourAgo })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return the proper data when the parameters are valid', async () => {
|
||||
// Note: this way all data will come as 0
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-status'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf(0);
|
||||
expect(body.success).to.be.true;
|
||||
});
|
||||
it('should return public rooms for a monitor with no units', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-status'))
|
||||
.set(agent2.credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true;
|
||||
});
|
||||
it('should return the proper data when login as a manager', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-status'))
|
||||
.set(credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf.greaterThan(0);
|
||||
expect(body.data.every((item: { value: number }) => item.value >= 0)).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/dashboards/conversations-by-department', () => {
|
||||
it('should return an error when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-reports', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(403);
|
||||
});
|
||||
it('should return an error when the start and end parameters are not provided', async () => {
|
||||
await restorePermissionToRoles('view-livechat-reports');
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-department')).set(credentials).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not provided', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not provided', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error if dates are more than 1 year apart', async () => {
|
||||
const oneYearAgo = new Date(Date.now() - 380 * 24 * 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ start: oneYearAgo, end: now })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when start is after end', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: oneHourAgo })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return the proper data when the parameters are valid', async () => {
|
||||
// Note: this way all data will come as 0
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf(0);
|
||||
expect(body.success).to.be.true;
|
||||
});
|
||||
it('should return public rooms for a monitor with no units', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(agent2.credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true;
|
||||
});
|
||||
it('should return the proper data when login as a manager', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-department'))
|
||||
.set(credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf.greaterThan(0);
|
||||
expect(body.data.every((item: { value: number }) => item.value >= 0)).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/dashboards/conversations-by-tags', () => {
|
||||
it('should return an error when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-reports', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-tags'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(403);
|
||||
});
|
||||
it('should return an error when the start and end parameters are not provided', async () => {
|
||||
await restorePermissionToRoles('view-livechat-reports');
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-tags')).set(credentials).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not provided', async () => {
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-tags')).set(credentials).query({ end: 'test' }).expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not provided', async () => {
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-tags')).set(credentials).query({ start: 'test' }).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-tags'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-tags'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error if dates are more than 1 year apart', async () => {
|
||||
const oneYearAgo = new Date(Date.now() - 380 * 24 * 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-tags'))
|
||||
.set(credentials)
|
||||
.query({ start: oneYearAgo, end: now })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when start is after end', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-tags'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: oneHourAgo })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return the proper data when the parameters are valid', async () => {
|
||||
// Note: this way all data will come as 0
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-tags'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf(0);
|
||||
expect(body.success).to.be.true;
|
||||
});
|
||||
it('should return public rooms for a monitor with no units', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-tags'))
|
||||
.set(agent2.credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true;
|
||||
});
|
||||
it('should return the proper data when login as a manager', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-tags'))
|
||||
.set(credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf.greaterThan(0);
|
||||
expect(body.data.every((item: { value: number }) => item.value >= 0)).to.be.true;
|
||||
});
|
||||
});
|
||||
describe('livechat/analytics/dashboards/conversations-by-agent', () => {
|
||||
it('should return an error when the user does not have the necessary permission', async () => {
|
||||
await updatePermission('view-livechat-reports', []);
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-agent'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(403);
|
||||
});
|
||||
it('should return an error when the start and end parameters are not provided', async () => {
|
||||
await restorePermissionToRoles('view-livechat-reports');
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-agent')).set(credentials).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not provided', async () => {
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-agent')).set(credentials).query({ end: 'test' }).expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not provided', async () => {
|
||||
await request.get(api('livechat/analytics/dashboards/conversations-by-agent')).set(credentials).query({ start: 'test' }).expect(400);
|
||||
});
|
||||
it('should return an error when the start parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-agent'))
|
||||
.set(credentials)
|
||||
.query({ start: 'test', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when the end parameter is not a valid date', async () => {
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-agent'))
|
||||
.set(credentials)
|
||||
.query({ start: '2020-01-01', end: 'test' })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error if dates are more than 1 year apart', async () => {
|
||||
const oneYearAgo = new Date(Date.now() - 380 * 24 * 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-agent'))
|
||||
.set(credentials)
|
||||
.query({ start: oneYearAgo, end: now })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return an error when start is after end', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-agent'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: oneHourAgo })
|
||||
.expect(400);
|
||||
});
|
||||
it('should return the proper data when the parameters are valid', async () => {
|
||||
// Note: this way all data will come as 0
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-agent'))
|
||||
.set(credentials)
|
||||
.query({ start: now, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf(0);
|
||||
});
|
||||
it('should return public rooms for a monitor with no units', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-agent'))
|
||||
.set(agent2.credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data.every((room: IOmnichannelRoom) => !room.departmentAncestors && !room.departmentId)).to.be.true;
|
||||
});
|
||||
it('should return the proper data when login as a manager', async () => {
|
||||
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/analytics/dashboards/conversations-by-agent'))
|
||||
.set(credentials)
|
||||
.query({ start: oneHourAgo, end: now })
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('data').and.to.be.an('array');
|
||||
expect(body.data).to.have.lengthOf.greaterThan(0);
|
||||
expect(body.data.every((item: { value: number }) => item.value >= 0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
301
apps/meteor/tests/end-to-end/api/livechat/22-monitors.ts
Normal file
301
apps/meteor/tests/end-to-end/api/livechat/22-monitors.ts
Normal file
@@ -0,0 +1,301 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import type { Credentials } from '@rocket.chat/api-client';
|
||||
import type { ILivechatDepartment, IUser } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { before, it, describe, after } from 'mocha';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { addOrRemoveAgentFromDepartment, createDepartment } from '../../../data/livechat/department';
|
||||
import {
|
||||
createAgent,
|
||||
createLivechatRoom,
|
||||
createManager,
|
||||
createVisitor,
|
||||
getLivechatRoomInfo,
|
||||
makeAgentAvailable,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import { createMonitor, createUnit } from '../../../data/livechat/units';
|
||||
import { updateSetting, updatePermission, restorePermissionToRoles, removePermissionFromAllRoles } from '../../../data/permissions.helper';
|
||||
import { password } from '../../../data/user';
|
||||
import { createUser, deleteUser, login, setUserActiveStatus } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
type TestUser = { user: IUser; credentials: Credentials };
|
||||
|
||||
(IS_EE ? describe : describe.skip)('Omnichannel - Monitors', () => {
|
||||
let manager: TestUser;
|
||||
let monitor: TestUser;
|
||||
let noUnitDepartment: ILivechatDepartment;
|
||||
let unitDepartment: ILivechatDepartment;
|
||||
|
||||
before((done) => getCredentials(done));
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
await setUserActiveStatus('rocketchat.internal.admin.test', true);
|
||||
await createAgent();
|
||||
await makeAgentAvailable();
|
||||
});
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
const userCredentials = await login(user.username, password);
|
||||
if (!user.username) {
|
||||
throw new Error('user not created');
|
||||
}
|
||||
await createManager(user.username);
|
||||
|
||||
manager = {
|
||||
user,
|
||||
credentials: userCredentials,
|
||||
};
|
||||
});
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
const userCredentials = await login(user.username, password);
|
||||
if (!user.username) {
|
||||
throw new Error('user not created');
|
||||
}
|
||||
await createMonitor(user.username);
|
||||
|
||||
monitor = {
|
||||
user,
|
||||
credentials: userCredentials,
|
||||
};
|
||||
});
|
||||
before(async () => {
|
||||
noUnitDepartment = await createDepartment();
|
||||
unitDepartment = await createDepartment();
|
||||
|
||||
await createUnit(monitor.user._id, monitor.user.username!, [unitDepartment._id]);
|
||||
});
|
||||
before(async () => {
|
||||
await updatePermission('transfer-livechat-guest', ['admin', 'livechat-manager', 'livechat-agent', 'livechat-monitor']);
|
||||
});
|
||||
|
||||
describe('Monitors', () => {
|
||||
let user: IUser;
|
||||
before(async () => {
|
||||
user = await createUser();
|
||||
});
|
||||
after(async () => {
|
||||
await deleteUser(user);
|
||||
});
|
||||
|
||||
it('should properly create a new monitor', async () => {
|
||||
const { body } = await request.post(api('livechat/monitors.create')).send({ username: user.username }).set(credentials).expect(200);
|
||||
|
||||
expect(body.success).to.be.true;
|
||||
});
|
||||
|
||||
it('should not fail when trying to create a monitor that already exists', async () => {
|
||||
const { body } = await request.post(api('livechat/monitors.create')).send({ username: user.username }).set(credentials).expect(200);
|
||||
|
||||
expect(body.success).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail when trying to create a monitor with an invalid username', async () => {
|
||||
const { body } = await request
|
||||
.post(api('livechat/monitors.create'))
|
||||
.set(credentials)
|
||||
.send({ username: 'invalid-username' })
|
||||
.expect(400);
|
||||
|
||||
expect(body.success).to.be.false;
|
||||
expect(body).to.have.property('error').to.be.equal('Invalid user [error-invalid-user]');
|
||||
});
|
||||
|
||||
it('should fail when trying to create a monitor with an empty username', async () => {
|
||||
const { body } = await request.post(api('livechat/monitors.create')).set(credentials).send({ username: '' }).expect(400);
|
||||
|
||||
expect(body.success).to.be.false;
|
||||
expect(body).to.have.property('error').to.be.equal('Invalid user [error-invalid-user]');
|
||||
});
|
||||
|
||||
it('should remove a monitor', async () => {
|
||||
const { body } = await request.post(api('livechat/monitors.delete')).set(credentials).send({ username: user.username }).expect(200);
|
||||
|
||||
expect(body.success).to.be.true;
|
||||
});
|
||||
|
||||
it('should fail when trying to remove a monitor that does not exist', async () => {
|
||||
const { body } = await request
|
||||
.post(api('livechat/monitors.delete'))
|
||||
.set(credentials)
|
||||
.send({ username: 'some-random-username' })
|
||||
.expect(400);
|
||||
|
||||
expect(body.success).to.be.false;
|
||||
expect(body).to.have.property('error').to.be.equal('Invalid user');
|
||||
});
|
||||
});
|
||||
|
||||
describe('[GET] livechat/monitors', () => {
|
||||
it('should fail if manage-livechat-monitors permission is missing', async () => {
|
||||
await removePermissionFromAllRoles('manage-livechat-monitors');
|
||||
return request.get(api('livechat/monitors')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return all monitors', async () => {
|
||||
await restorePermissionToRoles('manage-livechat-monitors');
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
|
||||
const { body } = await request.get(api('livechat/monitors')).set(credentials).query({ text: user.username }).expect(200);
|
||||
expect(body).to.have.property('monitors');
|
||||
expect(body.monitors).to.have.lengthOf(1);
|
||||
expect(body.monitors[0]).to.have.property('username', user.username);
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('livechat/monitors/:username', () => {
|
||||
it('should fail if manage-livechat-monitors permission is missing', async () => {
|
||||
await removePermissionFromAllRoles('manage-livechat-monitors');
|
||||
return request.get(api('livechat/monitors/123')).set(credentials).expect(403);
|
||||
});
|
||||
it('should return a monitor', async () => {
|
||||
await restorePermissionToRoles('manage-livechat-monitors');
|
||||
const user = await createUser();
|
||||
await createMonitor(user.username);
|
||||
|
||||
const { body } = await request
|
||||
.get(api(`livechat/monitors/${user.username}`))
|
||||
.set(credentials)
|
||||
.expect(200);
|
||||
expect(body).to.have.property('username', user.username);
|
||||
|
||||
// cleanup
|
||||
await deleteUser(user);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Monitors & Rooms', () => {
|
||||
it('should not return a room of a department that the monitor is not assigned to', async () => {
|
||||
const visitor = await createVisitor(noUnitDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/rooms'))
|
||||
.set(monitor.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('rooms').that.is.an('array');
|
||||
expect(body.rooms.find((r: any) => r._id === room._id)).to.not.exist;
|
||||
});
|
||||
it('should return a room of a department the monitor is assigned to', async () => {
|
||||
const visitor = await createVisitor(unitDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/rooms'))
|
||||
.set(monitor.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('rooms').that.is.an('array');
|
||||
expect(body.rooms.find((r: any) => r._id === room._id)).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Monitors & Departments', () => {
|
||||
it('should not return a department that the monitor is not assigned to', async () => {
|
||||
const { body } = await request
|
||||
.get(api('livechat/department'))
|
||||
.query({ onlyMyDepartments: true })
|
||||
.set(monitor.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('departments').that.is.an('array');
|
||||
expect(body.departments.find((d: any) => d._id === noUnitDepartment._id)).to.not.exist;
|
||||
});
|
||||
it('should return a department that the monitor is assigned to', async () => {
|
||||
const { body } = await request
|
||||
.get(api('livechat/department'))
|
||||
.query({ onlyMyDepartments: true })
|
||||
.set(monitor.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('departments').that.is.an('array');
|
||||
expect(body.departments.length).to.be.equal(1);
|
||||
expect(body.departments.find((d: any) => d._id === unitDepartment._id)).to.exist;
|
||||
});
|
||||
it('should return both created departments to a manager', async () => {
|
||||
const { body } = await request
|
||||
.get(api('livechat/department'))
|
||||
.query({ onlyMyDepartments: true, sort: '{ "_updatedAt": 1 }' })
|
||||
.set(manager.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('departments').that.is.an('array');
|
||||
expect(body.departments.find((d: any) => d._id === noUnitDepartment._id)).to.exist;
|
||||
expect(body.departments.find((d: any) => d._id === unitDepartment._id)).to.exist;
|
||||
});
|
||||
// OnlyMyDepartments to return data from units + the ones you serve in
|
||||
it.skip('should not return a department when monitor is only assigned as agent there', async () => {
|
||||
await createAgent(monitor.user.username!);
|
||||
await addOrRemoveAgentFromDepartment(
|
||||
noUnitDepartment._id,
|
||||
{ agentId: monitor.user._id, username: monitor.user.username!, count: 0, order: 0 },
|
||||
true,
|
||||
);
|
||||
|
||||
const { body } = await request
|
||||
.get(api('livechat/department'))
|
||||
.query({ onlyMyDepartments: true })
|
||||
.set(monitor.credentials)
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('departments').that.is.an('array');
|
||||
expect(body.departments.length).to.be.equal(1);
|
||||
expect(body.departments.find((d: any) => d._id === noUnitDepartment._id)).to.not.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Monitors & Forward', () => {
|
||||
it('should successfully forward a room to another agent', async () => {
|
||||
const visitor = await createVisitor(unitDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/room.forward'))
|
||||
.set(monitor.credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
userId: 'rocketchat.internal.admin.test',
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
|
||||
const room2 = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(room2).to.have.property('servedBy').that.is.an('object');
|
||||
expect(room2.servedBy).to.have.property('_id', 'rocketchat.internal.admin.test');
|
||||
});
|
||||
it('should successfully forward a room to a department', async () => {
|
||||
const visitor = await createVisitor(noUnitDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const { body } = await request
|
||||
.post(api('livechat/room.forward'))
|
||||
.set(monitor.credentials)
|
||||
.send({
|
||||
roomId: room._id,
|
||||
departmentId: unitDepartment._id,
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).to.have.property('success', true);
|
||||
|
||||
const room2 = await getLivechatRoomInfo(room._id);
|
||||
expect(room2.departmentId).to.be.equal(unitDepartment._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
134
apps/meteor/tests/end-to-end/api/livechat/23-mac.ts
Normal file
134
apps/meteor/tests/end-to-end/api/livechat/23-mac.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import type { ILivechatVisitor } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { before, afterEach, after, describe, it } from 'mocha';
|
||||
import moment from 'moment';
|
||||
|
||||
import { api, getCredentials, request, credentials } from '../../../data/api-data';
|
||||
import {
|
||||
createVisitor,
|
||||
createLivechatRoom,
|
||||
createAgent,
|
||||
makeAgentAvailable,
|
||||
sendAgentMessage,
|
||||
getLivechatRoomInfo,
|
||||
fetchInquiry,
|
||||
closeOmnichannelRoom,
|
||||
deleteVisitor,
|
||||
} from '../../../data/livechat/rooms';
|
||||
|
||||
describe('MAC', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
await createAgent();
|
||||
await makeAgentAvailable();
|
||||
});
|
||||
|
||||
describe('MAC rooms', () => {
|
||||
let visitor: ILivechatVisitor;
|
||||
let multipleContactsVisitor: ILivechatVisitor;
|
||||
|
||||
afterEach(() => deleteVisitor(visitor.token));
|
||||
|
||||
after(() => deleteVisitor(multipleContactsVisitor.token));
|
||||
|
||||
it('Should create an innactive room and contact by default', async () => {
|
||||
visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
expect(room).to.be.an('object');
|
||||
expect(room.v.activity).to.be.undefined;
|
||||
});
|
||||
|
||||
it('Should create an innactive contact by default', async () => {
|
||||
visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const res = await request.get(api(`omnichannel/contacts.get`)).set(credentials).query({ contactId: room.contactId });
|
||||
expect(res.body.contact.channels[0].visitor.visitorId).to.be.equal(visitor._id);
|
||||
expect(res.body.contact).not.to.have.property('activity');
|
||||
});
|
||||
|
||||
it('should mark room as active when agent sends a message', async () => {
|
||||
visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
await sendAgentMessage(room._id);
|
||||
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(updatedRoom).to.have.nested.property('v.activity').and.to.be.an('array');
|
||||
});
|
||||
|
||||
it('should mark contact as active when agent sends a message', async () => {
|
||||
multipleContactsVisitor = await createVisitor();
|
||||
const room = await createLivechatRoom(multipleContactsVisitor.token);
|
||||
|
||||
await sendAgentMessage(room._id);
|
||||
|
||||
const res = await request.get(api(`omnichannel/contacts.get`)).set(credentials).query({ contactId: room.contactId });
|
||||
expect(res.body.contact.channels[0].visitor.visitorId).to.be.equal(multipleContactsVisitor._id);
|
||||
expect(res.body.contact).to.have.property('activity').that.is.an('array').with.lengthOf(1);
|
||||
expect(res.body.contact.activity[0]).to.equal(moment.utc().format('YYYY-MM'));
|
||||
});
|
||||
|
||||
it('should mark multiple rooms as active when they come from same contact after an agent sends a message', async () => {
|
||||
const room = await createLivechatRoom(multipleContactsVisitor.token);
|
||||
|
||||
await sendAgentMessage(room._id);
|
||||
|
||||
const updatedRoom = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(updatedRoom).to.have.nested.property('v.activity').and.to.be.an('array');
|
||||
await closeOmnichannelRoom(room._id);
|
||||
});
|
||||
|
||||
it('should keep contact active when reusing it and an agent response is received', async () => {
|
||||
const room = await createLivechatRoom(multipleContactsVisitor.token);
|
||||
|
||||
await sendAgentMessage(room._id);
|
||||
|
||||
const res = await request.get(api(`omnichannel/contacts.get`)).set(credentials).query({ contactId: room.contactId });
|
||||
expect(res.body.contact.channels[0].visitor.visitorId).to.be.equal(multipleContactsVisitor._id);
|
||||
expect(res.body.contact).to.have.property('activity').that.is.an('array').with.lengthOf(1);
|
||||
expect(res.body.contact.activity[0]).to.equal(moment.utc().format('YYYY-MM'));
|
||||
await closeOmnichannelRoom(room._id);
|
||||
});
|
||||
|
||||
it('should mark room as active when it comes from same contact on same period, even without agent interaction', async () => {
|
||||
const room = await createLivechatRoom(multipleContactsVisitor.token);
|
||||
|
||||
expect(room).to.have.nested.property('v.activity').and.to.be.an('array');
|
||||
expect(room.v.activity?.includes(moment.utc().format('YYYY-MM'))).to.be.true;
|
||||
await closeOmnichannelRoom(room._id);
|
||||
});
|
||||
|
||||
it('should mark an inquiry as active when it comes from same contact on same period, even without agent interaction', async () => {
|
||||
const room = await createLivechatRoom(multipleContactsVisitor.token);
|
||||
const inquiry = await fetchInquiry(room._id);
|
||||
|
||||
expect(inquiry).to.have.nested.property('v.activity').and.to.be.an('array');
|
||||
expect(inquiry.v.activity?.includes(moment.utc().format('YYYY-MM'))).to.be.true;
|
||||
expect(room.v.activity?.includes(moment.utc().format('YYYY-MM'))).to.be.true;
|
||||
await closeOmnichannelRoom(room._id);
|
||||
});
|
||||
|
||||
it('contact should be marked as active for period', async () => {
|
||||
visitor = await createVisitor();
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
await sendAgentMessage(room._id);
|
||||
const res = await request.get(api(`omnichannel/contacts.get`)).set(credentials).query({ contactId: room.contactId });
|
||||
|
||||
expect(res.status).to.be.equal(200);
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.nested.property('contact').and.to.be.an('object');
|
||||
expect(res.body.contact.channels).to.be.an('array').with.lengthOf(1);
|
||||
expect(res.body.contact.channels[0].name).to.be.equal('api');
|
||||
expect(res.body.contact.channels[0].visitor.visitorId).to.be.equal(visitor._id);
|
||||
|
||||
expect(res.body.contact).to.have.nested.property('activity').and.to.be.an('array').with.lengthOf(1);
|
||||
expect(res.body.contact.activity[0]).to.equal(moment.utc().format('YYYY-MM'));
|
||||
await closeOmnichannelRoom(room._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
702
apps/meteor/tests/end-to-end/api/livechat/24-routing.ts
Normal file
702
apps/meteor/tests/end-to-end/api/livechat/24-routing.ts
Normal file
@@ -0,0 +1,702 @@
|
||||
import { faker } from '@faker-js/faker';
|
||||
import type { Credentials } from '@rocket.chat/api-client';
|
||||
import { UserStatus } from '@rocket.chat/core-typings';
|
||||
import type { ILivechatDepartment, IUser, ILivechatAgent } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { after, before, describe, it } from 'mocha';
|
||||
|
||||
import { getCredentials, request, api, credentials } from '../../../data/api-data';
|
||||
import {
|
||||
createAgent,
|
||||
makeAgentAvailable,
|
||||
createDepartment,
|
||||
createVisitor,
|
||||
createLivechatRoom,
|
||||
getLivechatRoomInfo,
|
||||
makeAgentUnavailable,
|
||||
switchLivechatStatus,
|
||||
} from '../../../data/livechat/rooms';
|
||||
import { getAgent } from '../../../data/livechat/users';
|
||||
import { getSettingValueById, updateSetting } from '../../../data/permissions.helper';
|
||||
import { password } from '../../../data/user';
|
||||
import { createUser, deleteUser, login, setUserActiveStatus, setUserStatus } from '../../../data/users.helper';
|
||||
import { IS_EE } from '../../../e2e/config/constants';
|
||||
|
||||
(IS_EE ? describe : describe.skip)('Omnichannel - Routing', () => {
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
|
||||
});
|
||||
|
||||
// Basically: if there's a bot in the department, it should be assigned to the conversation
|
||||
// No matter what settings workspace has in, as long as the setting to assign new conversations to bots is enabled
|
||||
describe('Bots - Manual selection', () => {
|
||||
let botUser: { user: IUser; credentials: Credentials };
|
||||
let testDepartment: ILivechatDepartment;
|
||||
let testDepartment2: ILivechatDepartment;
|
||||
before(async () => {
|
||||
const bot = await createUser({ roles: ['bot', 'livechat-agent'] });
|
||||
const credentials = await login(bot.username, password);
|
||||
|
||||
botUser = { user: bot, credentials };
|
||||
});
|
||||
before(async () => {
|
||||
testDepartment = await createDepartment([{ agentId: botUser.user._id }]);
|
||||
testDepartment2 = await createDepartment();
|
||||
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', true);
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(botUser.user);
|
||||
await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', false);
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', false);
|
||||
});
|
||||
|
||||
it('should assign conversation to bot', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(botUser.user._id);
|
||||
});
|
||||
it('should not assign conversation to bot if department has no bots', async () => {
|
||||
const visitor = await createVisitor(testDepartment2._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
expect(room.servedBy).to.be.undefined;
|
||||
});
|
||||
|
||||
describe('with setting disabled', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', false);
|
||||
});
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', true);
|
||||
});
|
||||
|
||||
it('should not assign conversation to bot', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
expect(room.servedBy).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bots - Auto selection', () => {
|
||||
let botUser: { user: IUser; credentials: Credentials };
|
||||
let otherUser: { user: IUser; credentials: Credentials };
|
||||
let testDepartment: ILivechatDepartment;
|
||||
let testDepartment2: ILivechatDepartment;
|
||||
before(async () => {
|
||||
const bot = await createUser({ roles: ['bot', 'livechat-agent'] });
|
||||
const credentials = await login(bot.username, password);
|
||||
|
||||
const other = await createUser({ roles: ['livechat-agent'] });
|
||||
const otherCredentials = await login(other.username, password);
|
||||
|
||||
await makeAgentAvailable(otherCredentials);
|
||||
|
||||
botUser = { user: bot, credentials };
|
||||
otherUser = { user: other, credentials: otherCredentials };
|
||||
});
|
||||
before(async () => {
|
||||
testDepartment = await createDepartment([{ agentId: botUser.user._id }, { agentId: otherUser.user._id }]);
|
||||
testDepartment2 = await createDepartment();
|
||||
await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', true);
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(botUser.user);
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', false);
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', false);
|
||||
});
|
||||
|
||||
it('should assign conversation to bot', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(botUser.user._id);
|
||||
});
|
||||
it('should not assign conversation to bot if department has no bots', async () => {
|
||||
const visitor = await createVisitor(testDepartment2._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
expect(room.servedBy).to.be.undefined;
|
||||
});
|
||||
|
||||
describe('with setting disabled', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', false);
|
||||
});
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', true);
|
||||
});
|
||||
|
||||
it('should not assign conversation to bot', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
expect(room.servedBy?._id).to.be.equal(otherUser.user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bots - Auto selection & Waiting queue', () => {
|
||||
let botUser: { user: IUser; credentials: Credentials };
|
||||
let testDepartment: ILivechatDepartment;
|
||||
let testDepartment2: ILivechatDepartment;
|
||||
before(async () => {
|
||||
const bot = await createUser({ roles: ['bot', 'livechat-agent'] });
|
||||
const credentials = await login(bot.username, password);
|
||||
|
||||
botUser = { user: bot, credentials };
|
||||
});
|
||||
before(async () => {
|
||||
testDepartment = await createDepartment([{ agentId: botUser.user._id }]);
|
||||
testDepartment2 = await createDepartment();
|
||||
await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
|
||||
await updateSetting('Livechat_waiting_queue', true);
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', true);
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(botUser.user);
|
||||
await updateSetting('Livechat_waiting_queue', false);
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', false);
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', false);
|
||||
});
|
||||
|
||||
it('should assign conversation to bot', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(botUser.user._id);
|
||||
});
|
||||
it('should not assign conversation to bot if department has no bots', async () => {
|
||||
const visitor = await createVisitor(testDepartment2._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
expect(room.servedBy).to.be.undefined;
|
||||
});
|
||||
|
||||
describe('with setting disabled', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', false);
|
||||
});
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_assign_new_conversation_to_bot', true);
|
||||
});
|
||||
|
||||
it('should not assign conversation to bot', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
expect(room.servedBy).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Auto-Selection', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_Routing_Method', 'Auto_Selection');
|
||||
});
|
||||
|
||||
let testUser: { user: IUser; credentials: Credentials };
|
||||
let testUser2: { user: IUser; credentials: Credentials };
|
||||
let testUser3: { user: IUser; credentials: Credentials };
|
||||
let testDepartment: ILivechatDepartment;
|
||||
let visitorEmail: string;
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentAvailable(credentials2);
|
||||
|
||||
testUser = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentUnavailable(credentials2);
|
||||
|
||||
testUser2 = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials3 = await login(user.username, password);
|
||||
await makeAgentAvailable(credentials3);
|
||||
|
||||
testUser3 = {
|
||||
user,
|
||||
credentials: credentials3,
|
||||
};
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
testDepartment = await createDepartment([{ agentId: testUser.user._id }, { agentId: testUser3.user._id }]);
|
||||
|
||||
const visitorName = faker.person.fullName();
|
||||
visitorEmail = faker.internet.email().toLowerCase();
|
||||
await request
|
||||
.post(api('omnichannel/contacts'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: visitorName,
|
||||
emails: [visitorEmail],
|
||||
phones: [],
|
||||
contactManager: testUser3.user._id,
|
||||
});
|
||||
});
|
||||
|
||||
after(async () =>
|
||||
Promise.all([
|
||||
deleteUser(testUser.user),
|
||||
deleteUser(testUser2.user),
|
||||
deleteUser(testUser3.user),
|
||||
updateSetting('Livechat_enabled_when_agent_idle', true),
|
||||
]),
|
||||
);
|
||||
|
||||
it('should route a room to an available agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id);
|
||||
expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id);
|
||||
});
|
||||
it('should not route to a not-available agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id);
|
||||
});
|
||||
it('should route to contact manager if it is online and Omnichannel_contact_manager_routing is enabled', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id, faker.person.fullName(), visitorEmail);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser3.user._id);
|
||||
});
|
||||
it('should fail to start a conversation if there is noone available and Livechat_accept_chats_with_no_agents is false', async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', false);
|
||||
await makeAgentUnavailable(testUser.credentials);
|
||||
await makeAgentUnavailable(testUser3.credentials);
|
||||
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }).expect(400);
|
||||
expect(body.error).to.be.equal('Sorry, no online agents [no-agent-online]');
|
||||
});
|
||||
it('should accept a conversation but not route to anyone when Livechat_accept_chats_with_no_agents is true', async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.undefined;
|
||||
});
|
||||
it('should not allow users to take more than Livechat_maximum_chats_per_agent chats', async () => {
|
||||
await updateSetting('Livechat_maximum_chats_per_agent', 2);
|
||||
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.undefined;
|
||||
});
|
||||
it('should ignore disabled users', async () => {
|
||||
await updateSetting('Livechat_maximum_chats_per_agent', 0);
|
||||
await setUserActiveStatus(testUser2.user._id, false);
|
||||
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
expect(roomInfo.servedBy).to.be.undefined;
|
||||
});
|
||||
it('should route to another available agent if contact manager is unavailable and Omnichannel_contact_manager_routing is enabled', async () => {
|
||||
await makeAgentAvailable(testUser.credentials);
|
||||
const visitor = await createVisitor(testDepartment._id, faker.person.fullName(), visitorEmail);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id);
|
||||
});
|
||||
describe('with setting Omnichannel_contact_manager_routing disabled', () => {
|
||||
let testDepartment2: ILivechatDepartment;
|
||||
before(async () => {
|
||||
await updateSetting('Omnichannel_contact_manager_routing', false);
|
||||
// We should update the count of the user
|
||||
});
|
||||
after(async () => {
|
||||
await updateSetting('Omnichannel_contact_manager_routing', true);
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
// This will deprioritize the CM from normal routing. So the test is predictable
|
||||
// IF the setting is off, the CM will be treated as a normal agent
|
||||
// IF the setting is on, the CM will be treated as a CM and no matter the count it will get assigned
|
||||
testDepartment2 = await createDepartment([{ agentId: testUser.user._id }, { agentId: testUser3.user._id, count: 10000 }]);
|
||||
|
||||
const visitorName = faker.person.fullName();
|
||||
visitorEmail = faker.internet.email().toLowerCase();
|
||||
await request
|
||||
.post(api('omnichannel/contacts'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
name: visitorName,
|
||||
emails: [visitorEmail],
|
||||
phones: [],
|
||||
contactManager: testUser3.user._id,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not route to contact manager if it is online but setting is off', async () => {
|
||||
const visitor = await createVisitor(testDepartment2._id, faker.person.fullName(), visitorEmail);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object').that.has.property('_id').that.is.not.equal(testUser3.user._id);
|
||||
});
|
||||
});
|
||||
describe('Auto_Selection - Livechat_accept_chats_with_no_agents', async () => {
|
||||
let initialSettingValue: any;
|
||||
let testUserInitialState: ILivechatAgent;
|
||||
let testUser3InitialState: ILivechatAgent;
|
||||
|
||||
before(async () => {
|
||||
testUserInitialState = await getAgent(testUser.user._id);
|
||||
testUser3InitialState = await getAgent(testUser3.user._id);
|
||||
initialSettingValue = await getSettingValueById('Livechat_accept_chats_with_no_agents');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', initialSettingValue);
|
||||
await switchLivechatStatus(testUserInitialState.statusLivechat, testUser.credentials);
|
||||
await switchLivechatStatus(testUser3InitialState.statusLivechat, testUser3.credentials);
|
||||
await setUserStatus(testUser3.credentials, testUser3InitialState.status);
|
||||
});
|
||||
describe('Livechat_accept_chats_with_no_agents is false', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', false);
|
||||
await switchLivechatStatus('not-available', testUser.credentials);
|
||||
await switchLivechatStatus('available', testUser3.credentials);
|
||||
await setUserStatus(testUser3.credentials, UserStatus.OFFLINE);
|
||||
});
|
||||
|
||||
it('should fail to start a conversation if there is an available but offline agent and Livechat_accept_chats_with_no_agents is false', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }).expect(400);
|
||||
expect(body.error).to.be.equal('Sorry, no online agents [no-agent-online]');
|
||||
});
|
||||
});
|
||||
describe('Livechat_accept_chats_with_no_agents is true', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
await switchLivechatStatus('available', testUser3.credentials);
|
||||
await setUserStatus(testUser3.credentials, UserStatus.OFFLINE);
|
||||
});
|
||||
it('should accept a conversation and route to an offline agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser3.user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Load Balancing', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_Routing_Method', 'Load_Balancing');
|
||||
});
|
||||
|
||||
let testUser: { user: IUser; credentials: Credentials };
|
||||
let testUser2: { user: IUser; credentials: Credentials };
|
||||
let testDepartment: ILivechatDepartment;
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentAvailable(credentials2);
|
||||
|
||||
testUser = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentUnavailable(credentials2);
|
||||
|
||||
testUser2 = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
testDepartment = await createDepartment([{ agentId: testUser.user._id }, { agentId: testUser2.user._id }]);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(testUser.user);
|
||||
await deleteUser(testUser2.user);
|
||||
});
|
||||
|
||||
it('should route a room to an available agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id);
|
||||
expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id);
|
||||
});
|
||||
it('should not route to a not-available agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id);
|
||||
});
|
||||
it('should route the chat to the less busy agent when both are available', async () => {
|
||||
await makeAgentAvailable(testUser2.credentials);
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser2.user._id);
|
||||
});
|
||||
it('should route again to the less busy agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser2.user._id);
|
||||
});
|
||||
describe('Load_Balancing - Livechat_accept_chats_with_no_agents', async () => {
|
||||
let initialSettingValue: any;
|
||||
let testUserInitialState: ILivechatAgent;
|
||||
let testUser2InitialState: ILivechatAgent;
|
||||
|
||||
before(async () => {
|
||||
testUserInitialState = await getAgent(testUser.user._id);
|
||||
testUser2InitialState = await getAgent(testUser2.user._id);
|
||||
initialSettingValue = await getSettingValueById('Livechat_accept_chats_with_no_agents');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', initialSettingValue);
|
||||
await switchLivechatStatus(testUserInitialState.statusLivechat, testUser.credentials);
|
||||
await switchLivechatStatus(testUser2InitialState.statusLivechat, testUser2.credentials);
|
||||
await setUserStatus(testUser2.credentials, testUser2InitialState.status);
|
||||
});
|
||||
describe('Livechat_accept_chats_with_no_agents is false', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', false);
|
||||
await switchLivechatStatus('not-available', testUser.credentials);
|
||||
await switchLivechatStatus('available', testUser2.credentials);
|
||||
await setUserStatus(testUser2.credentials, UserStatus.OFFLINE);
|
||||
});
|
||||
|
||||
it('should fail to start a conversation if there is an available but offline agent and Livechat_accept_chats_with_no_agents is false', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }).expect(400);
|
||||
expect(body.error).to.be.equal('Sorry, no online agents [no-agent-online]');
|
||||
});
|
||||
});
|
||||
describe('Livechat_accept_chats_with_no_agents is true', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
await switchLivechatStatus('available', testUser2.credentials);
|
||||
await setUserStatus(testUser2.credentials, UserStatus.OFFLINE);
|
||||
});
|
||||
it('should accept a conversation and route to an offline agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser2.user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Load Rotation', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_Routing_Method', 'Load_Rotation');
|
||||
});
|
||||
|
||||
let testUser: { user: IUser; credentials: Credentials };
|
||||
let testUser2: { user: IUser; credentials: Credentials };
|
||||
let testDepartment: ILivechatDepartment;
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentAvailable(credentials2);
|
||||
|
||||
testUser = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
const user = await createUser();
|
||||
await createAgent(user.username);
|
||||
const credentials2 = await login(user.username, password);
|
||||
await makeAgentUnavailable(credentials2);
|
||||
|
||||
testUser2 = {
|
||||
user,
|
||||
credentials: credentials2,
|
||||
};
|
||||
});
|
||||
|
||||
before(async () => {
|
||||
testDepartment = await createDepartment([{ agentId: testUser.user._id }, { agentId: testUser2.user._id }]);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteUser(testUser.user);
|
||||
await deleteUser(testUser2.user);
|
||||
});
|
||||
|
||||
it('should route a room to an available agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id);
|
||||
expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id);
|
||||
});
|
||||
it('should not route chat to an unavailable agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id);
|
||||
});
|
||||
it('should route the chat to the agent with the oldest routing time when both are available', async () => {
|
||||
await makeAgentAvailable(testUser2.credentials);
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser2.user._id);
|
||||
});
|
||||
it('should route again to the agent with the oldest routing time', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id);
|
||||
});
|
||||
describe('Load_Rotation - Livechat_accept_chats_with_no_agents', async () => {
|
||||
let initialSettingValue: any;
|
||||
let testUserInitialState: ILivechatAgent;
|
||||
let testUser2InitialState: ILivechatAgent;
|
||||
|
||||
before(async () => {
|
||||
testUserInitialState = await getAgent(testUser.user._id);
|
||||
testUser2InitialState = await getAgent(testUser2.user._id);
|
||||
initialSettingValue = await getSettingValueById('Livechat_accept_chats_with_no_agents');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', initialSettingValue);
|
||||
await switchLivechatStatus(testUserInitialState.statusLivechat, testUser.credentials);
|
||||
await switchLivechatStatus(testUser2InitialState.statusLivechat, testUser2.credentials);
|
||||
await setUserStatus(testUser2.credentials, testUser2InitialState.status);
|
||||
});
|
||||
describe('Livechat_accept_chats_with_no_agents is false', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', false);
|
||||
await switchLivechatStatus('not-available', testUser.credentials);
|
||||
await switchLivechatStatus('available', testUser2.credentials);
|
||||
await setUserStatus(testUser2.credentials, UserStatus.OFFLINE);
|
||||
});
|
||||
|
||||
it('should fail to start a conversation if there is an available but offline agent and Livechat_accept_chats_with_no_agents is false', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }).expect(400);
|
||||
expect(body.error).to.be.equal('Sorry, no online agents [no-agent-online]');
|
||||
});
|
||||
});
|
||||
describe('Livechat_accept_chats_with_no_agents is true', () => {
|
||||
before(async () => {
|
||||
await updateSetting('Livechat_accept_chats_with_no_agents', true);
|
||||
await switchLivechatStatus('available', testUser2.credentials);
|
||||
await setUserStatus(testUser2.credentials, UserStatus.OFFLINE);
|
||||
});
|
||||
it('should accept a conversation and route to an offline agent', async () => {
|
||||
const visitor = await createVisitor(testDepartment._id);
|
||||
const room = await createLivechatRoom(visitor.token);
|
||||
const roomInfo = await getLivechatRoomInfo(room._id);
|
||||
|
||||
expect(roomInfo.servedBy).to.be.an('object');
|
||||
expect(roomInfo.servedBy?._id).to.be.equal(testUser2.user._id);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
1881
apps/meteor/tests/end-to-end/api/livechat/contacts.ts
Normal file
1881
apps/meteor/tests/end-to-end/api/livechat/contacts.ts
Normal file
File diff suppressed because it is too large
Load Diff
297
apps/meteor/tests/end-to-end/api/livechat/custom-fields-save.ts
Normal file
297
apps/meteor/tests/end-to-end/api/livechat/custom-fields-save.ts
Normal file
@@ -0,0 +1,297 @@
|
||||
import type { ILivechatCustomField } from '@rocket.chat/core-typings';
|
||||
import { expect } from 'chai';
|
||||
import { after, before, describe, it } from 'mocha';
|
||||
import type { Response } from 'supertest';
|
||||
|
||||
import { getCredentials, api, request, credentials } from '../../../data/api-data';
|
||||
import { createCustomField, deleteCustomField } from '../../../data/livechat/custom-fields';
|
||||
import {
|
||||
getSettingValueById,
|
||||
removePermissionFromAllRoles,
|
||||
restorePermissionToRoles,
|
||||
updateSetting,
|
||||
} from '../../../data/permissions.helper';
|
||||
|
||||
describe('LIVECHAT - custom fields', () => {
|
||||
let settingLivechatEnabled: boolean;
|
||||
|
||||
before((done) => getCredentials(done));
|
||||
|
||||
before(async () => {
|
||||
settingLivechatEnabled = (await getSettingValueById('Livechat_enabled')) as boolean;
|
||||
await updateSetting('Livechat_enabled', true);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await updateSetting('Livechat_enabled', settingLivechatEnabled);
|
||||
});
|
||||
|
||||
describe('livechat/custom-fields.save', () => {
|
||||
let customFieldId: string;
|
||||
|
||||
after(async () => {
|
||||
if (customFieldId) {
|
||||
await deleteCustomField(customFieldId);
|
||||
}
|
||||
});
|
||||
|
||||
describe('Authentication/Authorization', () => {
|
||||
it('should return an "unauthenticated error" when user is not logged in', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.send({
|
||||
customFieldId: null,
|
||||
customFieldData: {
|
||||
field: 'test_field',
|
||||
label: 'Test Field',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(401);
|
||||
});
|
||||
|
||||
it('should return an "unauthorized error" when the user does not have the necessary permission', async () => {
|
||||
await removePermissionFromAllRoles('view-livechat-manager');
|
||||
|
||||
await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
customFieldId: null,
|
||||
customFieldData: {
|
||||
field: 'test_field',
|
||||
label: 'Test Field',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(403);
|
||||
|
||||
await restorePermissionToRoles('view-livechat-manager');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Create custom field', () => {
|
||||
afterEach(async () => {
|
||||
if (customFieldId) {
|
||||
await deleteCustomField(customFieldId);
|
||||
customFieldId = '';
|
||||
}
|
||||
});
|
||||
|
||||
it('should create a new custom field with minimal required fields', async () => {
|
||||
const fieldName = `field_${Date.now()}`;
|
||||
|
||||
await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
customFieldId: null,
|
||||
customFieldData: {
|
||||
field: fieldName,
|
||||
label: 'Test Field',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('customField');
|
||||
expect(res.body.customField).to.have.property('_id');
|
||||
expect(res.body.customField).to.have.property('label', 'Test Field');
|
||||
expect(res.body.customField).to.have.property('scope', 'visitor');
|
||||
expect(res.body.customField).to.have.property('visibility', 'public');
|
||||
customFieldId = res.body.customField._id;
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a new custom field with scope "room"', async () => {
|
||||
const fieldName = `room_field_${Date.now()}`;
|
||||
|
||||
await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
customFieldId: null,
|
||||
customFieldData: {
|
||||
field: fieldName,
|
||||
label: 'Room Test Field',
|
||||
scope: 'room',
|
||||
visibility: 'public',
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('customField');
|
||||
expect(res.body.customField).to.have.property('scope', 'room');
|
||||
customFieldId = res.body.customField._id;
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a new custom field with all optional fields', async () => {
|
||||
const fieldName = `full_field_${Date.now()}`;
|
||||
|
||||
await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
customFieldId: null,
|
||||
customFieldData: {
|
||||
field: fieldName,
|
||||
label: 'Full Test Field',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
type: 'input',
|
||||
regexp: '^[A-Za-z]+$',
|
||||
required: true,
|
||||
defaultValue: 'default',
|
||||
options: 'option1,option2,option3',
|
||||
public: true,
|
||||
searchable: true,
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body).to.have.property('customField');
|
||||
expect(res.body.customField).to.have.property('type', 'input');
|
||||
expect(res.body.customField).to.have.property('regexp', '^[A-Za-z]+$');
|
||||
expect(res.body.customField).to.have.property('required', true);
|
||||
expect(res.body.customField).to.have.property('defaultValue', 'default');
|
||||
expect(res.body.customField).to.have.property('options', 'option1,option2,option3');
|
||||
expect(res.body.customField).to.have.property('public', true);
|
||||
expect(res.body.customField).to.have.property('searchable', true);
|
||||
customFieldId = res.body.customField._id;
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when trying to create a custom field with a field name that already exists', async () => {
|
||||
const fieldName = `duplicate_field_${Date.now()}`;
|
||||
|
||||
// Create the first custom field
|
||||
const { body } = await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
customFieldId: null,
|
||||
customFieldData: {
|
||||
field: fieldName,
|
||||
label: 'First Field',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
customFieldId = body.customField._id;
|
||||
|
||||
// Try to create another with the same field name
|
||||
await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
customFieldId: null,
|
||||
customFieldData: {
|
||||
field: fieldName,
|
||||
label: 'Second Field',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Update custom field', () => {
|
||||
let existingField: ILivechatCustomField;
|
||||
|
||||
before(async () => {
|
||||
const fieldName = `update_test_field_${Date.now()}`;
|
||||
existingField = await createCustomField({
|
||||
searchable: true,
|
||||
field: fieldName,
|
||||
label: 'Original Label',
|
||||
defaultValue: 'original_default',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
regexp: '',
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
if (existingField?._id) {
|
||||
await deleteCustomField(existingField._id);
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail when trying to update a non-existent custom field', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
customFieldId: 'non-existent-id',
|
||||
customFieldData: {
|
||||
field: 'test_field',
|
||||
label: 'Updated Label',
|
||||
scope: 'visitor',
|
||||
visibility: 'public',
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(400)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', false);
|
||||
expect(res.body).to.have.property('error');
|
||||
});
|
||||
});
|
||||
|
||||
it('should update an existing custom field with all optional fields', async () => {
|
||||
await request
|
||||
.post(api('livechat/custom-fields.save'))
|
||||
.set(credentials)
|
||||
.send({
|
||||
customFieldId: existingField._id,
|
||||
customFieldData: {
|
||||
field: existingField._id,
|
||||
label: 'Fully Updated Field',
|
||||
scope: existingField.scope,
|
||||
visibility: 'public',
|
||||
type: 'select',
|
||||
regexp: '^[0-9]+$',
|
||||
required: true,
|
||||
defaultValue: 'new_default',
|
||||
options: 'opt1,opt2,opt3',
|
||||
public: true,
|
||||
searchable: false,
|
||||
},
|
||||
})
|
||||
.expect('Content-Type', 'application/json')
|
||||
.expect(200)
|
||||
.expect((res: Response) => {
|
||||
expect(res.body).to.have.property('success', true);
|
||||
expect(res.body.customField).to.have.property('label', 'Fully Updated Field');
|
||||
expect(res.body.customField).to.have.property('type', 'select');
|
||||
expect(res.body.customField).to.have.property('regexp', '^[0-9]+$');
|
||||
expect(res.body.customField).to.have.property('required', true);
|
||||
expect(res.body.customField).to.have.property('defaultValue', 'new_default');
|
||||
expect(res.body.customField).to.have.property('options', 'opt1,opt2,opt3');
|
||||
expect(res.body.customField).to.have.property('public', true);
|
||||
expect(res.body.customField).to.have.property('searchable', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user