import { faker } from '@faker-js/faker'; import type { ILivechatVisitor, IOmnichannelRoom } 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 { createCustomField, deleteCustomField } from '../../../data/livechat/custom-fields'; import { makeAgentAvailable, createAgent, createLivechatRoom, createVisitor, startANewLivechatRoomAndTakeIt, closeOmnichannelRoom, createVisitorWithCustomData, sendAgentMessage, sendMessage, } from '../../../data/livechat/rooms'; import { getRandomVisitorToken } from '../../../data/livechat/users'; import { getLivechatVisitorByToken } from '../../../data/livechat/visitor'; import { updatePermission, updateSetting, removePermissionFromAllRoles, restorePermissionToRoles, updateEESetting, } from '../../../data/permissions.helper'; import { adminUsername } from '../../../data/user'; import { IS_EE } from '../../../e2e/config/constants'; const getLicenseInfo = (loadValues = false) => { return request.get(api('licenses.info')).set(credentials).query({ loadValues }).expect(200); }; describe('LIVECHAT - visitors', () => { let visitor: ILivechatVisitor; before((done) => getCredentials(done)); before(async () => { await updateSetting('Livechat_enabled', true); await updatePermission('view-livechat-manager', ['admin']); await updateEESetting('Livechat_Require_Contact_Verification', 'never'); await createAgent(); await makeAgentAvailable(); visitor = await createVisitor(); }); describe('livechat/visitor', () => { it('should fail if no "visitor" key is passed as body parameter', async () => { const { body } = await request.post(api('livechat/visitor')).send({}); expect(body).to.have.property('success', false); }); it('should fail if visitor.token is not present', async () => { const { body } = await request.post(api('livechat/visitor')).send({ visitor: {} }); expect(body).to.have.property('success', false); }); it('should fail when token is an empty string', async () => { const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: '' } }); expect(body).to.have.property('success', false); }); it('should create a visitor', async () => { const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: 'test' } }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', 'test'); // Ensure all new visitors are created as online :) expect(body.visitor).to.have.property('status', 'online'); }); it('should create a visitor with provided extra information', async () => { const token = `${new Date().getTime()}-test`; const phone = new Date().getTime().toString(); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token, phone } }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', token); expect(body.visitor).to.have.property('phone'); expect(body.visitor.phone[0].phoneNumber).to.equal(phone); }); it('should save customFields when passed', async () => { const customFieldName = `new_custom_field_${Date.now()}`; const token = `${new Date().getTime()}-test`; await createCustomField({ searchable: true, field: customFieldName, label: customFieldName, defaultValue: 'test_default_address', scope: 'visitor', visibility: 'public', regexp: '', }); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token, customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }], }, }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', token); expect(body.visitor).to.have.property('livechatData'); expect(body.visitor.livechatData).to.have.property(customFieldName, 'Not a real address :)'); }); it('should update a current visitor when phone is same', async () => { const token = `${new Date().getTime()}-test`; const token2 = `${new Date().getTime()}-test2`; const phone = new Date().getTime().toString(); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token, phone } }); expect(body).to.have.property('success', true); const { body: body2 } = await request.post(api('livechat/visitor')).send({ visitor: { token: token2, phone, }, }); expect(body2).to.have.property('success', true); expect(body2).to.have.property('visitor'); // Same visitor won't update the token expect(body2.visitor).to.have.property('token', token); expect(body2.visitor).to.have.property('phone'); expect(body2.visitor.phone[0].phoneNumber).to.equal(phone); }); it('should update a visitor custom fields when customFields key is provided', async () => { const token = `${new Date().getTime()}-test`; 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/visitor')).send({ visitor: { token, customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }], }, }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', token); expect(body.visitor).to.have.property('livechatData'); expect(body.visitor.livechatData).to.have.property(customFieldName, 'Not a real address :)'); }); it('should not update a custom field when it does not exists', async () => { const token = `${new Date().getTime()}-test`; const customFieldName = `new_custom_field_${Date.now()}`; const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token, customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }], }, }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', token); expect(body.visitor).to.not.have.property('livechatData'); }); it('should not update a custom field when the scope of it is not visitor', async () => { const token = `${new Date().getTime()}-test`; const customFieldName = `new_custom_field_${Date.now()}`; await createCustomField({ searchable: true, field: customFieldName, label: customFieldName, defaultValue: 'test_default_address', scope: 'room', visibility: 'public', regexp: '', }); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token, customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }], }, }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', token); expect(body.visitor).to.not.have.property('livechatData'); }); it('should not update a custom field when the overwrite flag is false', async () => { const token = `${new Date().getTime()}-test`; const customFieldName = `new_custom_field_${Date.now()}`; await createCustomField({ searchable: true, field: customFieldName, label: customFieldName, defaultValue: 'test_default_address', scope: 'visitor', visibility: 'public', regexp: '', }); await request.post(api('livechat/visitor')).send({ visitor: { token, customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }], }, }); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token, customFields: [{ key: customFieldName, value: 'This should not change!', overwrite: false }], }, }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', token); expect(body.visitor).to.have.property('livechatData'); expect(body.visitor.livechatData).to.have.property(customFieldName, 'Not a real address :)'); }); it('should not validate required custom fields if no custom fields are provided', async () => { const token = `${new Date().getTime()}-test`; const customFieldName = `required_custom_field`; await createCustomField({ searchable: true, field: customFieldName, label: customFieldName, defaultValue: 'default_value', scope: 'visitor', visibility: 'public', regexp: '', required: true, }); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token } }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', token); await deleteCustomField(customFieldName); }); it('should fail if provided custom fields but are missing required ones', async () => { const token = `${new Date().getTime()}-test`; const optionalCustomFieldName = `optional_custom_field`; await createCustomField({ searchable: true, field: optionalCustomFieldName, label: optionalCustomFieldName, defaultValue: 'default_value', scope: 'visitor', visibility: 'public', regexp: '', required: false, }); const requiredCustomFieldName = `required_custom_field`; await createCustomField({ searchable: true, field: requiredCustomFieldName, label: requiredCustomFieldName, defaultValue: 'default_value', scope: 'visitor', visibility: 'public', regexp: '', required: true, }); const { body } = await request .post(api('livechat/visitor')) .send({ visitor: { token, customFields: [{ key: optionalCustomFieldName, value: 'test', overwrite: true }] } }); expect(body).to.have.property('success', false); expect(body).to.have.property('error'); expect(body.error).to.be.equal(`Missing required custom fields: required_custom_field`); await Promise.all([deleteCustomField(optionalCustomFieldName), deleteCustomField(requiredCustomFieldName)]); }); describe('special cases', () => { before(async () => { await updateSetting('Livechat_Allow_collect_and_store_HTTP_header_informations', true); }); after(async () => { await updateSetting('Livechat_Allow_collect_and_store_HTTP_header_informations', false); }); it('should allow to create a visitor without passing connectionData when GDPR setting is enabled', async () => { const token = `${new Date().getTime()}-test`; const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token } }); expect(body).to.have.property('success', true); expect(body).to.have.property('visitor'); expect(body.visitor).to.have.property('token', token); }); }); describe('visitor & contact custom fields', () => { let visitor: ILivechatVisitor; let room: IOmnichannelRoom; const cf1 = `cf1-${Date.now()}_1`; const cf2 = `cf2-${Date.now()}_2`; const cf3 = `cf3-${Date.now()}_3`; before(async () => { await createCustomField({ searchable: true, field: cf1, label: cf1, defaultValue: 'test_default_address', scope: 'visitor', visibility: 'public', regexp: '', }); await createCustomField({ searchable: true, field: cf2, label: cf2, defaultValue: 'test_default_address', scope: 'visitor', visibility: 'public', regexp: '', }); await createCustomField({ searchable: true, field: cf3, label: cf3, defaultValue: 'test_default_address', scope: 'visitor', visibility: 'public', regexp: '', }); }); after(async () => { await Promise.all([deleteCustomField(cf1), deleteCustomField(cf2), deleteCustomField(cf3)]); }); it('should update custom fields on the contact', async () => { const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: visitor.token, customFields: [{ key: cf1, value: 'test', overwrite: true }], }, }); expect(body).to.have.property('success', true); await request .get(api(`omnichannel/contacts.get`)) .set(credentials) .query({ contactId: room.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(cf1, 'test'); }); await closeOmnichannelRoom(room!._id); }); it('should update multiple custom fields on a contact after it already has custom fields added', async () => { const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: visitor.token, customFields: [{ key: cf1, value: 'test', overwrite: true }], }, }); expect(body).to.have.property('success', true); const { body: body2 } = await request.post(api('livechat/visitor')).send({ visitor: { token: visitor.token, customFields: [ { key: cf2, value: 'test', overwrite: true }, { key: cf3, value: 'test', overwrite: false }, ], }, }); expect(body2).to.have.property('success', true); await request .get(api(`omnichannel/contacts.get`)) .set(credentials) .query({ contactId: room.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(cf1, 'test'); expect(res.body.contact.customFields).to.have.property(cf2, 'test'); expect(res.body.contact.customFields).to.have.property(cf3, 'test'); }); await closeOmnichannelRoom(room!._id); }); it('should overwrite a custom field value when the flag is true', async () => { const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: visitor.token, customFields: [{ key: cf1, value: 'test', overwrite: true }], }, }); expect(body).to.have.property('success', true); const { body: body2 } = await request.post(api('livechat/visitor')).send({ visitor: { token: visitor.token, customFields: [ { key: cf1, value: 'new test', overwrite: true }, { key: cf3, value: 'test', overwrite: false }, ], }, }); expect(body2).to.have.property('success', true); await request .get(api(`omnichannel/contacts.get`)) .set(credentials) .query({ contactId: room.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(cf1, 'new test'); expect(res.body.contact.customFields).to.have.property(cf3, 'test'); }); await closeOmnichannelRoom(room!._id); }); it('should properly conflict a custom field when existing and overwrite is false', async () => { visitor = await createVisitor(); room = await createLivechatRoom(visitor.token); const { body } = await request.post(api('livechat/visitor')).send({ visitor: { token: visitor.token, customFields: [{ key: cf1, value: 'test', overwrite: true }], }, }); expect(body).to.have.property('success', true); const { body: body2 } = await request.post(api('livechat/visitor')).send({ visitor: { token: visitor.token, customFields: [ { key: cf1, value: 'new test', overwrite: false }, { key: cf2, value: 'test', overwrite: true }, ], }, }); expect(body2).to.have.property('success', true); await request .get(api(`omnichannel/contacts.get`)) .set(credentials) .query({ contactId: room.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(cf1, 'test'); expect(res.body.contact.customFields).to.have.property(cf2, 'test'); expect(res.body.contact.conflictingFields).to.be.an('array'); expect(res.body.contact.conflictingFields[0]) .to.be.an('object') .that.is.deep.equal({ field: `customFields.${cf1}`, value: 'new test', }); }); }); it('should add more conflicts to a contact custom fields', async () => { const { body: body2 } = await request.post(api('livechat/visitor')).send({ visitor: { token: visitor.token, customFields: [ { key: cf1, value: 'new test 2', overwrite: false }, { key: cf2, value: 'test', overwrite: true }, ], }, }); expect(body2).to.have.property('success', true); await request .get(api(`omnichannel/contacts.get`)) .set(credentials) .query({ contactId: room.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(cf1, 'test'); expect(res.body.contact.customFields).to.have.property(cf2, 'test'); expect(res.body.contact.conflictingFields).to.be.an('array').with.lengthOf(2); expect(res.body.contact.conflictingFields[0]) .to.be.an('object') .that.is.deep.equal({ field: `customFields.${cf1}`, value: 'new test', }); expect(res.body.contact.conflictingFields[1]) .to.be.an('object') .that.is.deep.equal({ field: `customFields.${cf1}`, value: 'new test 2', }); }); await closeOmnichannelRoom(room._id); }); }); }); describe('livechat/visitors.info', () => { 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/visitors.info')) .query({ visitorId: 'invalid' }) .set(credentials) .expect('Content-Type', 'application/json') .expect(403) .expect((res: Response) => { expect(res.body).to.have.property('success', false); expect(res.body.error).to.be.equal('User does not have the permissions required for this action [error-unauthorized]'); }); }); it('should return an "visitor not found error" when the visitor doe snot exists', async () => { await updatePermission('view-l-room', ['admin']); await request .get(api('livechat/visitors.info')) .query({ visitorId: 'invalid' }) .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('visitor-not-found'); }); }); it('should return the visitor info', async () => { await request .get(api('livechat/visitors.info')) .query({ visitorId: visitor._id }) .set(credentials) .expect('Content-Type', 'application/json') .expect(200) .expect((res: Response) => { expect(res.body).to.have.property('success', true); expect(res.body.visitor._id).to.be.equal(visitor._id); }); }); it('should not return the visitor token', async () => { await request .get(api('livechat/visitors.info')) .query({ visitorId: visitor._id }) .set(credentials) .expect('Content-Type', 'application/json') .expect(200) .expect((res: Response) => { expect(res.body).to.have.property('success', true); expect(res.body.visitor).to.not.have.property('token'); }); }); }); describe('livechat/visitors.pagesVisited', () => { 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/visitors.pagesVisited/room-id')) .set(credentials) .expect('Content-Type', 'application/json') .expect(403) .expect((res: Response) => { expect(res.body).to.have.property('success', false); expect(res.body.error).to.be.equal('User does not have the permissions required for this action [error-unauthorized]'); }); }); it('should return an "error" when the roomId param is not provided', async () => { await updatePermission('view-l-room', ['admin']); await request .get(api('livechat/visitors.pagesVisited/room-id')) .set(credentials) .expect('Content-Type', 'application/json') .expect(400) .expect((res: Response) => { expect(res.body).to.have.property('success', false); }); }); it('should return an array of pages', async () => { await updatePermission('view-l-room', ['admin']); const createdVisitor = await createVisitor(); const createdRoom = await createLivechatRoom(createdVisitor.token); await request .get(api(`livechat/visitors.pagesVisited/${createdRoom._id}`)) .set(credentials) .expect('Content-Type', 'application/json') .expect(200) .expect((res: Response) => { expect(res.body).to.have.property('success', true); expect(res.body.pages).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/visitors.chatHistory/room/room-id/visitor/visitor-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/visitors.chatHistory/room/room-id/visitor/visitor-id')) .set(credentials) .expect('Content-Type', 'application/json') .expect(403) .expect((res: Response) => { expect(res.body).to.have.property('success', false); expect(res.body.error).to.be.equal('User does not have the permissions required for this action [error-unauthorized]'); }); }); it('should return an "error" when the roomId param is invalid', async () => { await updatePermission('view-l-room', ['admin']); await request .get(api('livechat/visitors.chatHistory/room/room-id/visitor/visitor-id')) .set(credentials) .expect('Content-Type', 'application/json') .expect(400) .expect((res: Response) => { expect(res.body).to.have.property('success', false); }); }); it('should return an array of chat history', async () => { await updatePermission('view-l-room', ['admin']); const createdVisitor = await createVisitor(); const createdRoom = await createLivechatRoom(createdVisitor.token); await request .get(api(`livechat/visitors.chatHistory/room/${createdRoom._id}/visitor/${createdVisitor._id}`)) .set(credentials) .expect('Content-Type', 'application/json') .expect(200) .expect((res: Response) => { expect(res.body).to.have.property('success', true); expect(res.body.history).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/visitor/:token', () => { // get it("should return a 'invalid token' error when visitor with given token does not exist ", async () => { await request .get(api('livechat/visitor/invalid')) .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-token]'); }); }); it('should return an error when the "token" query parameter is not valid', async () => { await request .get(api('livechat/visitor/invalid')) .set(credentials) .expect('Content-Type', 'application/json') .expect(400) .expect((res: Response) => { expect(res.body).to.have.property('success', false); }); }); it('should return a visitor when the query params is all valid', async () => { await request .get(api(`livechat/visitor/${visitor.token}`)) .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('visitor'); }); }); // delete it("should return a 'invalid token' error when visitor with given token does not exist ", async () => { await request .delete(api('livechat/visitor/invalid')) .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-token]'); }); }); it('should return an error when the "token" query parameter is not valid', async () => { await request .delete(api('livechat/visitor/invalid')) .set(credentials) .expect('Content-Type', 'application/json') .expect(400) .expect((res: Response) => { expect(res.body).to.have.property('success', false); }); }); it("should return a 'visitor-has-open-rooms' error when there are open rooms", async () => { await updateSetting('Livechat_Allow_collect_and_store_HTTP_header_informations', false); const createdVisitor = await createVisitor(); await createLivechatRoom(createdVisitor.token); await request .delete(api(`livechat/visitor/${createdVisitor.token}`)) .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('Cannot remove visitors with opened rooms [visitor-has-open-rooms]'); }); }); it("should not return a 'visitor-has-open-rooms' when visitor has open rooms but GDPR is enabled", async () => { await updateSetting('Livechat_Allow_collect_and_store_HTTP_header_informations', true); const createdVisitor = await createVisitor(); await createLivechatRoom(createdVisitor.token); await request .delete(api(`livechat/visitor/${createdVisitor.token}`)) .set(credentials) .expect('Content-Type', 'application/json') .expect(200); }); it('should remove the rooms associated with the visitor if any', async () => { const createdVisitor = await createVisitor(); const room = await createLivechatRoom(createdVisitor.token); await request .delete(api(`livechat/visitor/${createdVisitor.token}`)) .set(credentials) .expect('Content-Type', 'application/json') .expect(200); await request.get(api('livechat/room')).query({ rid: room._id }).set(credentials).expect(400); }); it('should remove the contact associated with the visitor if any', async () => { const createdVisitor = await createVisitor(); const room = await createLivechatRoom(createdVisitor.token); await request .delete(api(`livechat/visitor/${createdVisitor.token}`)) .set(credentials) .expect('Content-Type', 'application/json') .expect(200); await request.get(api(`omnichannel/contacts.get`)).set(credentials).query({ contactId: room.contactId }).expect(404); }); it('should return a visitor when the query params is all valid', async () => { const createdVisitor = await createVisitor(); await request .delete(api(`livechat/visitor/${createdVisitor.token}`)) .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('visitor'); expect(res.body.visitor).to.have.property('_id'); expect(res.body.visitor).to.have.property('ts'); expect(res.body.visitor._id).to.be.equal(createdVisitor._id); }); }); it('should not affect MAC count when a visitor is removed via GDPR', async () => { const { visitor, room } = await startANewLivechatRoomAndTakeIt(); // agent should send a message on the room await request .post(api('chat.sendMessage')) .set(credentials) .send({ message: { rid: room._id, msg: 'test', }, }); const { body: currentLicense } = await getLicenseInfo(true); await request .delete(api(`livechat/visitor/${visitor.token}`)) .set(credentials) .expect('Content-Type', 'application/json') .expect(200); const { body: licenseAfterGdpr } = await getLicenseInfo(true); expect(currentLicense.license).to.have.property('limits'); expect(currentLicense.license.limits).to.have.property('monthlyActiveContacts'); expect(currentLicense.license.limits.monthlyActiveContacts).to.have.property('value'); const currentLimit = currentLicense.license.limits.monthlyActiveContacts.value; expect(licenseAfterGdpr.license).to.have.property('limits'); expect(licenseAfterGdpr.license.limits).to.have.property('monthlyActiveContacts'); expect(licenseAfterGdpr.license.limits.monthlyActiveContacts).to.have.property('value'); const limitAfterGdpr = licenseAfterGdpr.license.limits.monthlyActiveContacts.value; expect(limitAfterGdpr).to.be.equal(currentLimit); }); it("should return a 'error-removing-visitor' error when removeGuest's result is false", async () => { await request .delete(api('livechat/visitor/123')) .set(credentials) .expect('Content-Type', 'application/json') .expect(400) .expect((res: Response) => { expect(res.body).to.have.property('success', false); }); }); }); describe('livechat/visitors.autocomplete', () => { it('should return an error when the user doesnt have the right permissions', async () => { await updatePermission('view-l-room', []); await request .get(api('livechat/visitors.autocomplete')) .set(credentials) .query({ selector: 'invalid' }) .query({ selector: 'xxx' }) .expect('Content-Type', 'application/json') .expect(403); }); it('should return an error when the "selector" query parameter is not valid', async () => { await updatePermission('view-l-room', ['admin', 'livechat-agent']); await request .get(api('livechat/visitors.autocomplete')) .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("must have required property 'selector' [invalid-params]"); }); }); it('should return an error if "selector" param is not JSON serializable', async () => { await updatePermission('view-l-room', ['admin', 'livechat-agent']); await request .get(api('livechat/visitors.autocomplete')) .query({ selector: '{invalid' }) .set(credentials) .expect('Content-Type', 'application/json') .expect(400) .expect((res: Response) => { expect(res.body).to.have.property('success', false); }); }); it('should return a list of visitors when the query params is all valid', async () => { await updatePermission('view-l-room', ['admin', 'livechat-agent']); const createdVisitor = await createVisitor(); await request .get(api('livechat/visitors.autocomplete')) .query({ selector: JSON.stringify({ term: createdVisitor.name }) }) .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('items'); expect(res.body.items).to.be.an('array'); expect(res.body.items).to.have.length.of.at.least(1); expect(res.body.items[0]).to.have.property('_id'); expect(res.body.items[0]).to.have.property('name'); const visitor = res.body.items.find((item: any) => item._id === createdVisitor._id); expect(visitor).to.have.property('_id'); expect(visitor).to.have.property('name'); expect(visitor._id).to.be.equal(createdVisitor._id); }); }); }); describe('livechat/visitors.searchChats/room/:roomId/visitor/:visitorId', () => { it('should return an error when the user doesnt have the right permissions', async () => { await updatePermission('view-l-room', []); await request .get(api('livechat/visitors.searchChats/room/123/visitor/123')) .set(credentials) .expect('Content-Type', 'application/json') .expect(403); }); it('should throw an error when the roomId is not valid', async () => { await updatePermission('view-l-room', ['admin', 'livechat-agent']); await request .get(api('livechat/visitors.searchChats/room/invalid/visitor/123')) .set(credentials) .expect('Content-Type', 'application/json') .expect(400) .expect((res: Response) => { expect(res.body).to.have.property('success', false); }); }); it('should return an empty array if the user is not the one serving the chat', async () => { await updatePermission('view-l-room', ['admin', 'livechat-agent']); await updateSetting('Livechat_Routing_Method', 'Manual_Selection'); const createdVisitor = await createVisitor(); const room = await createLivechatRoom(createdVisitor.token); await request .get(api(`livechat/visitors.searchChats/room/${room._id}/visitor/123`)) .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('history'); expect(res.body.history).to.be.an('array'); expect(res.body.history).to.have.lengthOf(0); }); }); it('should return an empty array if the visitorId doesnt correlate to room', async () => { await updatePermission('view-l-room', ['admin', 'livechat-agent']); await updateSetting('Livechat_Routing_Method', 'Manual_Selection'); const createdVisitor = await createVisitor(); const room = await createLivechatRoom(createdVisitor.token); await request .get(api(`livechat/visitors.searchChats/room/${room._id}/visitor/123`)) .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('history'); expect(res.body.history).to.be.an('array'); expect(res.body.history).to.have.lengthOf(0); }); }); it('should return a list of chats when the query params is all valid', async () => { await updatePermission('view-l-room', ['admin', 'livechat-agent', 'livechat-manager']); await createAgent(); const { room: { _id: roomId }, visitor: { _id: visitorId }, } = await startANewLivechatRoomAndTakeIt(); await request .get(api(`livechat/visitors.searchChats/room/${roomId}/visitor/${visitorId}?closedChatsOnly=false&servedChatsOnly=true`)) .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('history'); expect(res.body.history).to.be.an('array'); expect(res.body.history).to.have.length.of.at.least(1); expect(res.body.history[0]).to.have.property('_id'); expect(res.body.history[0]).to.have.property('fname'); expect(res.body.history[0]).to.have.property('v'); }); }); it('should return a list of chats when filtered by ', async () => { const { room: { _id: roomId }, visitor: { _id: visitorId }, } = await startANewLivechatRoomAndTakeIt(); await request .get(api(`livechat/visitors.searchChats/room/${roomId}/visitor/${visitorId}?source=api&servedChatsOnly=true`)) .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('history'); expect(res.body.history).to.be.an('array'); expect(res.body.history).to.have.length.of.at.least(1); expect(res.body.history[0]).to.have.property('_id'); expect(res.body.history[0]).to.have.property('fname'); expect(res.body.history[0]).to.have.property('v'); }); }); it('should return only closed chats when closedChatsOnly is true', async () => { const { room: { _id: roomId }, visitor: { _id: visitorId }, } = await startANewLivechatRoomAndTakeIt(); await closeOmnichannelRoom(roomId); await request .get(api(`livechat/visitors.searchChats/room/${roomId}/visitor/${visitorId}?closedChatsOnly=true&servedChatsOnly=false`)) .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('history'); expect(res.body.history).to.be.an('array'); expect(res.body.history.find((chat: any) => chat._id === roomId)).to.be.an('object'); }); }); it('should return only served chats when servedChatsOnly is true', async () => { const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); await request .get(api(`livechat/visitors.searchChats/room/${room._id}/visitor/${visitor._id}?closedChatsOnly=false&servedChatsOnly=true`)) .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('history'); expect(res.body.history).to.be.an('array'); expect(res.body.history.find((chat: any) => chat._id === room._id)).to.be.undefined; }); }); it('should return closed rooms (served & unserved) when `closedChatsOnly` is true & `servedChatsOnly` is false', async () => { const { room: { _id: roomId }, visitor: { _id: visitorId, token }, } = await startANewLivechatRoomAndTakeIt(); await closeOmnichannelRoom(roomId); const room2 = await createLivechatRoom(token); await closeOmnichannelRoom(room2._id); await request .get(api(`livechat/visitors.searchChats/room/${roomId}/visitor/${visitorId}?closedChatsOnly=true&servedChatsOnly=false`)) .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('history'); expect(res.body.history).to.be.an('array'); expect(res.body.history.find((chat: any) => chat._id === roomId)).to.be.an('object'); expect(res.body.history.find((chat: any) => chat._id === room2._id)).to.be.an('object'); }); }); it('should return all chats when both closed & served flags are false', async () => { const visitor = await createVisitor(); const room = await createLivechatRoom(visitor.token); await closeOmnichannelRoom(room._id); const room2 = await createLivechatRoom(visitor.token); await closeOmnichannelRoom(room2._id); await createLivechatRoom(visitor.token); const { body } = await request .get(api(`livechat/visitors.searchChats/room/${room._id}/visitor/${visitor._id}?closedChatsOnly=false&servedChatsOnly=false`)) .set(credentials) .expect('Content-Type', 'application/json') .expect(200); expect(body).to.have.property('success', true); expect(body.count).to.be.equal(3); expect(body.history.filter((chat: any) => !!chat.closedAt).length === 2).to.be.true; expect(body.history.filter((chat: any) => !chat.closedAt).length === 1).to.be.true; expect(body.total).to.be.equal(3); }); }); describe('livechat/visitor.status', () => { it('should return an error if token is not present as body param', async () => { const res = await request.post(api('livechat/visitor.status')).set(credentials).expect(400); expect(res.body).to.have.property('success', false); }); it('should return an error if status is not present as body param', async () => { const res = await request.post(api('livechat/visitor.status')).set(credentials).send({ token: '123' }).expect(400); expect(res.body).to.have.property('success', false); }); it('should return an error if token is not a valid guest token', async () => { const res = await request.post(api('livechat/visitor.status')).set(credentials).send({ token: '123', status: 'online' }).expect(400); expect(res.body).to.have.property('success', false); }); it('should update visitor status if all things are valid', async () => { const visitor = await createVisitor(); const res = await request .post(api('livechat/visitor.status')) .set(credentials) .send({ token: visitor.token, status: 'online' }) .expect(200); expect(res.body).to.have.property('success', true); }); }); describe('GET [omnichannel/contact.search]', () => { it('should fail if no email|phone|custom params are passed as query', async () => { await request.get(api('omnichannel/contact.search')).set(credentials).expect('Content-Type', 'application/json').expect(400); }); it('should fail if its trying to find by an empty string', async () => { await request .get(api('omnichannel/contact.search')) .query({ email: '' }) .set(credentials) .expect('Content-Type', 'application/json') .expect(400); }); it('should fail if custom is passed but is not JSON serializable', async () => { await request .get(api('omnichannel/contact.search')) .query({ custom: '{a":1}' }) .set(credentials) .expect('Content-Type', 'application/json') .expect(400); }); it('should fail if custom is an empty object and no email|phone are provided', async () => { await request .get(api('omnichannel/contact.search')) .query({ custom: '{}' }) .set(credentials) .expect('Content-Type', 'application/json') .expect(400); }); it('should find a contact by email', async () => { const visitor = await createVisitor(); await request .get(api('omnichannel/contact.search')) .query({ email: visitor.visitorEmails?.[0].address }) .set(credentials) .send() .expect('Content-Type', 'application/json') .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('_id'); expect(res.body.contact).to.have.property('name'); expect(res.body.contact).to.have.property('username'); expect(res.body.contact).to.have.property('phone'); expect(res.body.contact).to.have.property('visitorEmails'); expect(res.body.contact._id).to.be.equal(visitor._id); expect(res.body.contact.phone[0].phoneNumber).to.be.equal(visitor.phone?.[0].phoneNumber); // done(); }); }); it('should find a contact by phone', async () => { const visitor = await createVisitor(); await request .get(api('omnichannel/contact.search')) .query({ phone: visitor.phone?.[0].phoneNumber }) .set(credentials) .send() .expect('Content-Type', 'application/json') .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('_id'); expect(res.body.contact).to.have.property('name'); expect(res.body.contact).to.have.property('username'); expect(res.body.contact).to.have.property('phone'); expect(res.body.contact).to.have.property('visitorEmails'); expect(res.body.contact._id).to.be.equal(visitor._id); expect(res.body.contact.phone[0].phoneNumber).to.be.equal(visitor.phone?.[0].phoneNumber); }); }); it('should find a contact by custom field', async () => { const cfID = 'address'; const cf = await createCustomField({ searchable: true, field: 'address', label: 'address', defaultValue: 'test_default_address', scope: 'visitor', visibility: 'public', regexp: '', }); if (!cf) { throw new Error('Custom field not created'); } await createVisitor(); await request .get(api('omnichannel/contact.search')) .query({ custom: JSON.stringify({ address: 'Rocket.Chat' }) }) .set(credentials) .send() .expect('Content-Type', 'application/json') .expect(200) .expect((res: Response) => { expect(res.body).to.have.property('success', true); expect(res.body.contact).to.have.property('name'); expect(res.body.contact).to.have.property('username'); expect(res.body.contact).to.have.property('phone'); expect(res.body.contact).to.have.property('visitorEmails'); expect(res.body.contact.livechatData).to.have.property('address', 'Rocket.Chat street'); }); await deleteCustomField(cfID); }); it('should return null if an invalid set of custom fields is passed and no other params are sent', async () => { const res = await request .get(api('omnichannel/contact.search')) .query({ custom: JSON.stringify({ nope: 'nel' }) }) .set(credentials) .send(); expect(res.body).to.have.property('success', true); expect(res.body.contact).to.be.null; }); it('should not break if more than 1 custom field are passed', async () => { const res = await request .get(api('omnichannel/contact.search')) .query({ custom: JSON.stringify({ nope: 'nel', another: 'field' }) }) .set(credentials) .send(); expect(res.body).to.have.property('success', true); expect(res.body.contact).to.be.null; }); it('should not break if bad things are passed as custom field keys', async () => { const res = await request .get(api('omnichannel/contact.search')) .query({ custom: JSON.stringify({ $regex: 'nel' }) }) .set(credentials) .send(); expect(res.body).to.have.property('success', true); expect(res.body.contact).to.be.null; }); it('should not break if bad things are passed as custom field keys 2', async () => { const res = await request .get(api('omnichannel/contact.search')) .query({ custom: JSON.stringify({ '$regex: { very-bad }': 'nel' }) }) .set(credentials) .send(); expect(res.body).to.have.property('success', true); expect(res.body.contact).to.be.null; }); it('should not break if bad things are passed as custom field values', async () => { const res = await request .get(api('omnichannel/contact.search')) .query({ custom: JSON.stringify({ nope: '^((ab)*)+$' }) }) .set(credentials) .send(); expect(res.body).to.have.property('success', true); expect(res.body.contact).to.be.null; }); }); describe('livechat/visitors.search', () => { it('should fail if user doesnt have view-l-room permission', async () => { await updatePermission('view-l-room', []); const res = await request.get(api('livechat/visitors.search')).query({ text: 'nel' }).set(credentials).send(); expect(res.body).to.have.property('success', false); }); it('should fail if term is not on query params', async () => { await updatePermission('view-l-room', ['admin', 'livechat-agent']); const res = await request.get(api(`livechat/visitors.search`)).set(credentials).send(); expect(res.body).to.have.property('success', false); }); it('should not fail when term is an evil regex string', async () => { const res = await request.get(api('livechat/visitors.search')).query({ term: '^((ab)*)+$' }).set(credentials).send(); expect(res.body).to.have.property('success', true); }); it('should return a list of visitors when term is a valid string', async () => { const visitor = await createVisitor(); const res = await request.get(api('livechat/visitors.search')).query({ term: visitor.name }).set(credentials).send(); expect(res.body).to.have.property('success', true); expect(res.body.visitors).to.be.an('array'); expect(res.body.visitors).to.have.lengthOf.greaterThan(0); expect(res.body.visitors[0]).to.have.property('_id', visitor._id); expect(res.body.visitors[0]).to.have.property('name', visitor.name); expect(res.body.visitors[0]).to.have.property('username', visitor.username); expect(res.body.visitors[0]).to.have.property('visitorEmails'); expect(res.body.visitors[0]).to.have.property('phone'); }); it('should return a list of visitors when term is an empty string', async () => { const res = await request.get(api('livechat/visitors.search')).query({ term: '' }).set(credentials).send(); expect(res.body).to.have.property('success', true); expect(res.body.visitors).to.be.an('array'); expect(res.body.visitors).to.have.lengthOf.greaterThan(0); expect(res.body.visitors[0]).to.have.property('_id'); expect(res.body.visitors[0]).to.have.property('username'); expect(res.body.visitors[0]).to.have.property('name'); expect(res.body.visitors[0]).to.have.property('phone'); expect(res.body.visitors[0]).to.have.property('visitorEmails'); }); }); describe('omnichannel/contact', () => { let contact: ILivechatVisitor; it('should fail if user doesnt have view-l-room permission', async () => { await removePermissionFromAllRoles('view-l-room'); const res = await request.get(api('omnichannel/contact')).query({ text: 'nel' }).set(credentials).send(); expect(res.body).to.have.property('success', false); await restorePermissionToRoles('view-l-room'); }); it('should create a new contact', async () => { const token = getRandomVisitorToken(); const res = await request.post(api('omnichannel/contact')).set(credentials).send({ name: faker.person.fullName(), token, }); expect(res.body).to.have.property('success', true); expect(res.body).to.have.property('contact'); expect(res.body.contact).to.be.an('string'); const contactId: string = res.body.contact; contact = await getLivechatVisitorByToken(token); expect(contact._id).to.equal(contactId); }); it('should update an existing contact', async () => { const name = faker.person.fullName(); const res = await request.post(api('omnichannel/contact')).set(credentials).send({ name, token: contact.token, }); expect(res.body).to.have.property('success', true); expect(res.body).to.have.property('contact'); expect(res.body.contact).to.be.an('string'); expect(res.body.contact).to.equal(contact._id); contact = await getLivechatVisitorByToken(contact.token); expect(contact.name).to.equal(name); }); it('should change the contact name, email and phone', async () => { const name = faker.person.fullName(); const email = faker.internet.email().toLowerCase(); const phone = faker.phone.number(); const res = await request.post(api('omnichannel/contact')).set(credentials).send({ name, email, phone, token: contact.token, }); expect(res.body).to.have.property('success', true); expect(res.body).to.have.property('contact'); expect(res.body.contact).to.be.an('string'); expect(res.body.contact).to.equal(contact._id); contact = await getLivechatVisitorByToken(contact.token); expect(contact.name).to.equal(name); expect(contact.visitorEmails).to.be.an('array'); expect(contact.visitorEmails).to.have.lengthOf(1); if (contact.visitorEmails?.[0]) { expect(contact.visitorEmails[0].address).to.equal(email); } expect(contact.phone).to.be.an('array'); expect(contact.phone).to.have.lengthOf(1); if (contact.phone?.[0]) { expect(contact.phone[0].phoneNumber).to.equal(phone); } }); (IS_EE ? it : it.skip)('should change the contact manager', async () => { const managerUsername = adminUsername; const res = await request .post(api('omnichannel/contact')) .set(credentials) .send({ contactManager: { username: managerUsername, }, token: contact.token, name: contact.name, }); expect(res.body).to.have.property('success', true); expect(res.body).to.have.property('contact'); expect(res.body.contact).to.be.an('string'); expect(res.body.contact).to.equal(contact._id); contact = await getLivechatVisitorByToken(contact.token); expect(contact.contactManager).to.be.an('object'); expect(contact.contactManager).to.have.property('username', managerUsername); }); it('should change custom fields', async () => { const cfName = faker.lorem.word(); await createCustomField({ searchable: true, field: cfName, label: cfName, scope: 'visitor', visibility: 'visible', regexp: '', }); const res = await request .post(api('omnichannel/contact')) .set(credentials) .send({ token: contact.token, name: contact.name, customFields: { [cfName]: 'test', }, }); expect(res.body).to.have.property('success', true); expect(res.body).to.have.property('contact'); expect(res.body.contact).to.be.an('string'); expect(res.body.contact).to.equal(contact._id); contact = await getLivechatVisitorByToken(contact.token); expect(contact).to.have.property('livechatData'); expect(contact.livechatData).to.have.property(cfName, 'test'); }); }); describe('lead capture', () => { let visitor: ILivechatVisitor; let room: IOmnichannelRoom; before(async () => { visitor = await createVisitorWithCustomData({ ignoreEmail: true, ignorePhone: true, }); room = await createLivechatRoom(visitor.token); await createAgent(); }); after(async () => { await closeOmnichannelRoom(room._id); }); it('should capture the data matching the email regex and add it to the visitor', async () => { await sendMessage(room._id, 'My email is random@email.com', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.visitorEmails).to.be.an('array'); expect(visitor.visitorEmails).to.have.lengthOf(1); expect(visitor.visitorEmails?.[0].address).to.be.equal('random@email.com'); }); it('should capture more emails when the visitor sends the message', async () => { await sendMessage(room._id, 'Another email is test@teste.com', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.visitorEmails).to.be.an('array'); expect(visitor.visitorEmails).to.have.lengthOf(2); const emails = visitor.visitorEmails?.map((e) => e.address); expect(emails).to.include('test@teste.com'); }); it('should capture multiple emails', async () => { await sendMessage(room._id, 'My emails are test@123.com notest@1234.com', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.visitorEmails).to.be.an('array'); expect(visitor.visitorEmails).to.have.lengthOf(4); const emails = visitor.visitorEmails?.map((e) => e.address); expect(emails).to.include('test@123.com'); expect(emails).to.include('notest@1234.com'); }); it('should not add an email thats already registered', async () => { await sendMessage(room._id, 'My email is test@123.com', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.visitorEmails).to.be.an('array'); expect(visitor.visitorEmails).to.have.lengthOf(4); }); it('should not save emails the agent sends', async () => { await sendAgentMessage(room._id, 'Confirming your email is test@12345.com?', credentials); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.visitorEmails).to.be.an('array'); expect(visitor.visitorEmails).to.have.lengthOf(4); }); it('should save phone numbers matching the regex', async () => { await sendMessage(room._id, 'My phone number is 12345678', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.phone).to.be.an('array'); expect(visitor.phone).to.have.lengthOf(1); expect(visitor.phone?.[0].phoneNumber).to.be.equal('12345678'); }); it('should capture more phone numbers when the visitor sends the message', async () => { await sendMessage(room._id, 'Another phone number is 87654321 87654323', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.phone).to.be.an('array'); expect(visitor.phone).to.have.lengthOf(3); const phones = visitor.phone?.map((p) => p.phoneNumber); expect(phones).to.include('87654321'); expect(phones).to.include('87654323'); }); it('should not add a phone number thats already registered', async () => { await sendMessage(room._id, 'My phone number is 12345678', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.phone).to.be.an('array'); expect(visitor.phone).to.have.lengthOf(3); }); it('should not save phone numbers the agent sends', async () => { await sendAgentMessage(room._id, 'Confirming your phone number is 99999999?', credentials); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.phone).to.be.an('array'); expect(visitor.phone).to.have.lengthOf(3); }); it('should capture both phones & emails when sent on the same message', async () => { await sendMessage(room._id, 'My email is zardw@asdf.com and my phone is 11223344', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.visitorEmails).to.be.an('array'); expect(visitor.visitorEmails).to.have.lengthOf(5); const emails = visitor.visitorEmails?.map((e) => e.address); expect(emails).to.include('zardw@asdf.com'); expect(visitor.phone).to.be.an('array'); expect(visitor.phone).to.have.lengthOf(4); const phones = visitor.phone?.map((p) => p.phoneNumber); expect(phones).to.include('11223344'); }); describe('when settings are empty', () => { before(async () => { await updateSetting('Livechat_lead_phone_regex', ''); await updateSetting('Livechat_lead_email_regex', ''); }); after(async () => { // reset settings await updateSetting('Livechat_lead_email_regex', '\\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\\.)+[A-Z]{2,4}\\b'); await updateSetting( 'Livechat_lead_phone_regex', '((?:\\([0-9]{1,3}\\)|[0-9]{2})[ \\-]*?[0-9]{4,5}(?:[\\-\\s\\_]{1,2})?[0-9]{4}(?:(?=[^0-9])|$)|[0-9]{4,5}(?:[\\-\\s\\_]{1,2})?[0-9]{4}(?:(?=[^0-9])|$))', ); }); it('should not capture any email or phone no matter what the visitor sends', async () => { await sendMessage(room._id, 'Now my email is this@shalnotpass.com', visitor.token); await sendMessage(room._id, 'And my phone number is 55667788', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); const emails = visitor.visitorEmails?.map((e) => e.address); expect(emails || []).to.not.include('this@shalnotpass.com'); const phones = visitor.phone?.map((p) => p.phoneNumber); expect(phones || []).to.not.include('55667788'); }); }); describe('when phone regex is broken', () => { before(async () => { await updateSetting('Livechat_lead_phone_regex', '('); }); after(async () => { // reset settings await updateSetting( 'Livechat_lead_phone_regex', '((?:\\([0-9]{1,3}\\)|[0-9]{2})[ \\-]*?[0-9]{4,5}(?:[\\-\\s\\_]{1,2})?[0-9]{4}(?:(?=[^0-9])|$)|[0-9]{4,5}(?:[\\-\\s\\_]{1,2})?[0-9]{4}(?:(?=[^0-9])|$))', ); }); it('should capture email', async () => { await sendMessage(room._id, 'Now my email is this@isok.com', visitor.token); await sendMessage(room._id, 'And my phone number is 55667799', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.visitorEmails).to.be.an('array'); expect(visitor.visitorEmails).to.have.lengthOf(6); const emails = visitor.visitorEmails?.map((e) => e.address); expect(emails).to.include('this@isok.com'); expect(visitor.phone).to.have.lengthOf(4); const phones = visitor.phone?.map((p) => p.phoneNumber); expect(phones || []).to.not.include('55667788'); }); }); describe('when email regex is broken', () => { before(async () => { await updateSetting('Livechat_lead_email_regex', '('); }); after(async () => { // reset settings await updateSetting('Livechat_lead_email_regex', '\\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\\.)+[A-Z]{2,4}\\b'); }); it('should capture email', async () => { await sendMessage(room._id, 'Now my email is this@shalnotpass.com', visitor.token); await sendMessage(room._id, 'And my phone number is 98765432', visitor.token); visitor = await getLivechatVisitorByToken(visitor.token); expect(visitor.visitorEmails).to.have.lengthOf(6); const emails = visitor.visitorEmails?.map((e) => e.address); expect(emails || []).to.not.include('this@shalnotpass.com'); expect(visitor.phone).to.be.an('array'); expect(visitor.phone).to.have.lengthOf(5); const phones = visitor.phone?.map((p) => p.phoneNumber); expect(phones).to.include('98765432'); }); }); describe('when the visitor has emails & phones already', () => { let newVisitor: ILivechatVisitor; let newRoom: IOmnichannelRoom; before(async () => { newVisitor = await createVisitor(); newRoom = await createLivechatRoom(newVisitor.token); }); after(async () => { await closeOmnichannelRoom(newRoom._id); }); it('should capture new emails & phones and add them to the existing ones', async () => { await sendMessage(newRoom._id, 'My email is 1234@12344.com and my phone is 11223344', newVisitor.token); newVisitor = await getLivechatVisitorByToken(newVisitor.token); expect(newVisitor.visitorEmails).to.be.an('array'); expect(newVisitor.visitorEmails).to.have.lengthOf(2); const emails = newVisitor.visitorEmails?.map((e) => e.address); expect(emails).to.include('1234@12344.com'); expect(newVisitor.phone).to.be.an('array'); expect(newVisitor.phone).to.have.lengthOf(2); const phones = newVisitor.phone?.map((p) => p.phoneNumber); expect(phones).to.include('11223344'); }); }); }); });