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, }, ); }); }); });