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

This commit is contained in:
upstream-mirror
2026-09-16 01:21:02 +05:00
commit 30810eecd7
10161 changed files with 987110 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
import { createFakeDepartment } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelAgents } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createDepartment } from '../utils/omnichannel/departments';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe.serial('OC - Manage Agents', () => {
let poOmnichannelAgents: OmnichannelAgents;
let department: Awaited<ReturnType<typeof createDepartment>>;
// Create agent and department
test.beforeEach(async ({ api }) => {
department = await createDepartment(api);
});
test.beforeEach(async ({ page }) => {
poOmnichannelAgents = new OmnichannelAgents(page);
await poOmnichannelAgents.goto();
});
test.beforeAll(async ({ api }) => {
expect((await setSettingValueById(api, 'Omnichannel_enable_department_removal', true)).status()).toBe(200);
});
test.afterAll(async ({ api }) => {
expect((await setSettingValueById(api, 'Omnichannel_enable_department_removal', false)).status()).toBe(200);
});
// Ensure that there is no leftover data even if test fails
test.afterEach(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await department.delete();
});
test('OC - Manage Agents - Add, search and remove using table', async ({ page }) => {
await test.step('expect "user1" be first ', async () => {
await poOmnichannelAgents.inputUsername.type('user');
await expect(page.locator('role=option[name="user1"]')).toContainText('user1');
await poOmnichannelAgents.inputUsername.fill('');
});
await test.step('expect add "user1" as agent', async () => {
await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAddAgent.click();
await poOmnichannelAgents.search('user1');
await expect(poOmnichannelAgents.table.findRowByName('user1')).toBeVisible();
});
await test.step('expect remove "user1" as agent', async () => {
await poOmnichannelAgents.search('user1');
await poOmnichannelAgents.deleteAgent('user1');
await poOmnichannelAgents.search('user1');
await expect(poOmnichannelAgents.table.findRowByName('user1')).not.toBeVisible();
});
});
test('OC - Manage Agents [CE]- Edit and Remove', async () => {
test.skip(IS_EE, 'Community Edition Only');
await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAddAgent.click();
await poOmnichannelAgents.search('user1');
await poOmnichannelAgents.table.findRowByName('user1').click();
await poOmnichannelAgents.agentInfo.btnEdit.click();
await test.step('expect max chats fields to be hidden', async () => {
await expect(poOmnichannelAgents.editAgent.inputMaxChats).toBeHidden();
});
await test.step('expect update "user1" information', async () => {
await poOmnichannelAgents.editAgent.selectStatus('Not Available');
await poOmnichannelAgents.editAgent.selectDepartment(department.data.name);
await poOmnichannelAgents.editAgent.save();
});
await test.step('expect removing "user1" via sidebar', async () => {
await poOmnichannelAgents.search('user1');
await poOmnichannelAgents.table.findRowByName('user1').click();
await poOmnichannelAgents.agentInfo.btnRemove.click();
});
});
test('OC - Manage Agents [EE] - Edit ', async () => {
test.skip(!IS_EE, 'Enterprise Only');
await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAddAgent.click();
await poOmnichannelAgents.search('user1');
await poOmnichannelAgents.table.findRowByName('user1').click();
await poOmnichannelAgents.agentInfo.btnEdit.click();
await test.step('expect max chats field to be visible', async () => {
await expect(poOmnichannelAgents.editAgent.inputMaxChats).toBeVisible();
});
await test.step('expect update "user1" information', async () => {
await poOmnichannelAgents.editAgent.inputMaxChats.click();
await poOmnichannelAgents.editAgent.inputMaxChats.fill('2');
await poOmnichannelAgents.editAgent.btnSave.click();
});
});
test('OC - Edit agent - Manage departments', async ({ page }) => {
await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAddAgent.click();
await poOmnichannelAgents.search('user1');
await poOmnichannelAgents.table.findRowByName('user1').click();
await poOmnichannelAgents.agentInfo.btnEdit.click();
await poOmnichannelAgents.editAgent.selectDepartment(department.data.name);
const response = page.waitForResponse('**/api/v1/livechat/agents.saveInfo');
await poOmnichannelAgents.editAgent.btnSave.click();
/**
* between saving and opening the agent info again it is necessary to
* wait for the agent to be saved, since after successfully saving
* the contextual bar is closed
* otherwise content will be closed even if the current one is not the editing one
*/
await response;
await expect(poOmnichannelAgents.editAgent.root).not.toBeVisible();
await test.step('expect the selected department is visible', async () => {
await poOmnichannelAgents.table.findRowByName('user1').click();
// mock the endpoint to use the one without pagination
await page.route('/api/v1/livechat/department?showArchived=true', async (route) => {
await route.fulfill({ json: { departments: [] } });
});
await poOmnichannelAgents.agentInfo.btnEdit.click();
await expect(poOmnichannelAgents.editAgent.findSelectedDepartment(department.data.name)).toBeVisible();
});
});
test('OC - Edit agent - Departments pagination', async ({ page }) => {
const data = Array.from({ length: 100 }, (_, i) => createFakeDepartment({ name: `Department ${i}` }));
await page.route('**/v1/livechat/department*', async (route, request) => {
const url = new URL(request.url());
const offset = parseInt(url.searchParams.get('offset') || '0', 10);
const count = parseInt(url.searchParams.get('count') || '50', 10);
const departments = data.slice(offset, offset + count);
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ departments, offset, count, total: data.length }),
});
});
await test.step('expect to add agent', async () => {
await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAddAgent.click();
});
await test.step('expect to edit agent', async () => {
await poOmnichannelAgents.search('user1');
await poOmnichannelAgents.table.findRowByName('user1').click();
await poOmnichannelAgents.agentInfo.btnEdit.click();
});
await test.step('expect department field to be paginated', async () => {
await poOmnichannelAgents.editAgent.inputDepartment.click();
await expect(poOmnichannelAgents.editAgent.getDepartmentOption('Department 0')).toBeVisible();
await poOmnichannelAgents.scrollToListBottom();
await expect(poOmnichannelAgents.editAgent.getDepartmentOption('Department 49')).toBeVisible();
await poOmnichannelAgents.scrollToListBottom();
await expect(poOmnichannelAgents.editAgent.getDepartmentOption('Department 99')).toBeVisible();
await poOmnichannelAgents.editAgent.close();
});
await test.step('expect remove "user1" as agent', async () => {
await poOmnichannelAgents.search('user1');
await poOmnichannelAgents.deleteAgent('user1');
await poOmnichannelAgents.search('user1');
await expect(poOmnichannelAgents.table.findRowByName('user1')).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,102 @@
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelLivechatAppearance } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe.serial('OC - Livechat Appearance - EE', () => {
let poLivechatAppearance: OmnichannelLivechatAppearance;
test.skip(!IS_EE, 'Enterprise Only');
test.beforeEach(async ({ page }) => {
poLivechatAppearance = new OmnichannelLivechatAppearance(page);
await poLivechatAppearance.goto();
});
test.afterAll(async ({ api }) => {
const res = await Promise.all([
api.post('/settings/Livechat_hide_system_messages', { value: ['uj', 'ul', 'livechat-close'] }),
api.post('/settings/Livechat_background', { value: '' }),
]);
if (res.some((r) => r.status() !== 200)) {
throw new Error('Failed to reset settings');
}
});
test('OC - Livechat Appearance - Hide system messages', async ({ page }) => {
await test.step('expect to have default values', async () => {
await poLivechatAppearance.inputHideSystemMessages.click();
await expect(poLivechatAppearance.findHideSystemMessageOption('uj')).toHaveAttribute('aria-selected', 'true');
await expect(poLivechatAppearance.findHideSystemMessageOption('ul')).toHaveAttribute('aria-selected', 'true');
await expect(poLivechatAppearance.findHideSystemMessageOption('livechat-close')).toHaveAttribute('aria-selected', 'true');
await poLivechatAppearance.inputHideSystemMessages.click();
});
await test.step('expect to change values', async () => {
await poLivechatAppearance.inputHideSystemMessages.click();
await poLivechatAppearance.findHideSystemMessageOption('livechat_transfer_history').click();
await poLivechatAppearance.findHideSystemMessageOption('livechat-close').click();
await poLivechatAppearance.btnSaveChanges.click();
});
await test.step('expect to have saved changes', async () => {
await page.reload();
await poLivechatAppearance.inputHideSystemMessages.click();
await expect(poLivechatAppearance.findHideSystemMessageOption('uj')).toHaveAttribute('aria-selected', 'true');
await expect(poLivechatAppearance.findHideSystemMessageOption('ul')).toHaveAttribute('aria-selected', 'true');
await expect(poLivechatAppearance.findHideSystemMessageOption('livechat_transfer_history')).toHaveAttribute('aria-selected', 'true');
await expect(poLivechatAppearance.findHideSystemMessageOption('livechat-close')).toHaveAttribute('aria-selected', 'false');
});
});
test('OC - Livechat Appearance - Change Livechat background', async ({ page }) => {
await test.step('expect to have default value', async () => {
await expect(await poLivechatAppearance.inputLivechatBackground).toHaveValue('');
});
await test.step('expect to change value', async () => {
await poLivechatAppearance.inputLivechatBackground.fill('rgb(186, 1, 85)');
await poLivechatAppearance.btnSaveChanges.click();
});
await test.step('expect to have saved changes', async () => {
await page.reload();
await expect(poLivechatAppearance.inputLivechatBackground).toHaveValue('rgb(186, 1, 85)');
});
});
});
test.describe('OC - Livechat Appearance - CE', () => {
let poLivechatAppearance: OmnichannelLivechatAppearance;
test.beforeEach(async ({ page }) => {
poLivechatAppearance = new OmnichannelLivechatAppearance(page);
await poLivechatAppearance.goto();
});
test.afterAll(async ({ api }) => {
const res = await api.post('/settings/Livechat_title', { value: 'Rocket.Chat' });
if (res.status() !== 200) {
throw new Error('Failed to reset settings');
}
});
test('OC - Livechat Appearance - Change Livechat Title', async ({ page }) => {
await test.step('expect to have default value', async () => {
await expect(poLivechatAppearance.inputLivechatTitle).toHaveValue('Rocket.Chat');
});
await test.step('expect to change value', async () => {
await poLivechatAppearance.inputLivechatTitle.fill('Test Title');
await poLivechatAppearance.btnSaveChanges.click();
});
await test.step('expect to have saved changes', async () => {
await page.reload();
await expect(poLivechatAppearance.inputLivechatTitle).toHaveValue('Test Title');
});
});
});

View File

@@ -0,0 +1,147 @@
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { setSettingValueById } from '../utils';
import { createAgent } from '../utils/omnichannel/agents';
import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments';
import { createConversation } from '../utils/omnichannel/rooms';
import { createTag } from '../utils/omnichannel/tags';
import { test, expect } from '../utils/test';
const visitorA = createFakeVisitor();
const visitorB = createFakeVisitor();
test.use({ storageState: Users.user1.state });
test.describe('OC - Tags Visibility', () => {
test.skip(!IS_EE, 'OC - Tags Visibility > Enterprise Edition Only');
let poOmnichannel: HomeOmnichannel;
let conversations: Awaited<ReturnType<typeof createConversation>>[] = [];
let departmentA: Awaited<ReturnType<typeof createDepartment>>;
let departmentB: Awaited<ReturnType<typeof createDepartment>>;
let agent: Awaited<ReturnType<typeof createAgent>>;
let tagA: Awaited<ReturnType<typeof createTag>>;
let tagB: Awaited<ReturnType<typeof createTag>>;
let globalTag: Awaited<ReturnType<typeof createTag>>;
let sharedTag: Awaited<ReturnType<typeof createTag>>;
test.beforeAll('Create departments', async ({ api }) => {
expect((await setSettingValueById(api, 'Omnichannel_enable_department_removal', true)).status()).toBe(200);
departmentA = await createDepartment(api, { name: 'Department A' });
departmentB = await createDepartment(api, { name: 'Department B' });
});
test.beforeAll('Create agent', async ({ api }) => {
agent = await createAgent(api, 'user1');
});
test.beforeAll('Add agents to departments', async ({ api }) => {
await Promise.all([
addAgentToDepartment(api, { department: departmentA.data, agentId: 'user1' }),
addAgentToDepartment(api, { department: departmentB.data, agentId: 'user1' }),
]);
});
test.beforeAll('Create tags', async ({ api }) => {
tagA = await createTag(api, { departments: [departmentA.data._id] });
tagB = await createTag(api, { departments: [departmentB.data._id] });
globalTag = await createTag(api, { departments: [] });
sharedTag = await createTag(api, {
departments: [departmentA.data._id, departmentB.data._id],
});
});
test.beforeEach(async ({ page }) => {
poOmnichannel = new HomeOmnichannel(page);
await poOmnichannel.goto();
});
test.beforeEach(async ({ api }) => {
if (conversations.length === 0) {
conversations = await Promise.all([
createConversation(api, {
visitorName: visitorA.name,
agentId: 'user1',
departmentId: departmentA.data._id,
}),
createConversation(api, {
visitorName: visitorB.name,
agentId: 'user1',
departmentId: departmentB.data._id,
}),
]);
}
});
test.afterAll(async ({ api }) => {
await Promise.all(conversations.map((conversation) => conversation.delete()));
await Promise.all([tagA, tagB, globalTag, sharedTag].map((tag) => tag.delete()));
await agent.delete();
await departmentA.delete();
await departmentB.delete();
expect((await setSettingValueById(api, 'Omnichannel_enable_department_removal', false)).status()).toBe(200);
});
test('Verify agent should see correct tags based on department association', async () => {
await test.step('Agent opens room', async () => {
await poOmnichannel.navbar.openChat(visitorA.name);
});
await test.step('should not be able to see tags field', async () => {
await expect(poOmnichannel.roomInfo.getLabel('Tags')).not.toBeVisible();
});
await test.step('check available tags', async () => {
await poOmnichannel.roomInfo.btnEdit.click();
await expect(poOmnichannel.editRoomInfo.root).toBeVisible();
await poOmnichannel.editRoomInfo.inputTags.click();
});
await test.step('Should see TagA (department A specific)', async () => {
await expect(poOmnichannel.editRoomInfo.optionTag(tagA.data.name)).toBeVisible();
});
await test.step('Should see SharedTag (both departments)', async () => {
await expect(poOmnichannel.editRoomInfo.optionTag(sharedTag.data.name)).toBeVisible();
});
await test.step('Should see Public Tags for all chats (no department restriction)', async () => {
await expect(poOmnichannel.editRoomInfo.optionTag(globalTag.data.name)).toBeVisible();
});
await test.step('Should not see TagB (department B specific)', async () => {
await expect(poOmnichannel.editRoomInfo.optionTag(tagB.data.name)).not.toBeVisible();
});
await test.step('add tags and save', async () => {
await poOmnichannel.editRoomInfo.selectTag(tagA.data.name);
await poOmnichannel.editRoomInfo.selectTag(globalTag.data.name);
await poOmnichannel.editRoomInfo.btnSave.click();
});
await test.step('verify selected tags are displayed under room information', async () => {
await expect(poOmnichannel.roomInfo.getLabel('Tags')).toBeVisible();
await expect(poOmnichannel.roomInfo.getTagInfoByLabel(tagA.data.name)).toBeVisible();
await expect(poOmnichannel.roomInfo.getTagInfoByLabel(globalTag.data.name)).toBeVisible();
});
});
test('Verify tags visibility for agent associated with multiple departments', async () => {
await test.step('Open room info', async () => {
await poOmnichannel.navbar.openChat(visitorB.name);
await poOmnichannel.roomInfo.btnEdit.click();
await expect(poOmnichannel.editRoomInfo.root).toBeVisible();
await poOmnichannel.editRoomInfo.inputTags.click();
});
await test.step('Agent associated with DepartmentB should be able to see tags for Department B', async () => {
await expect(poOmnichannel.editRoomInfo.optionTag(tagB.data.name)).toBeVisible();
});
await test.step('Agent associated with DepartmentB should not be able to see tags for DepartmentA', async () => {
await expect(poOmnichannel.editRoomInfo.optionTag(tagA.data.name)).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,83 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe('omnichannel-auto-onhold-chat-closing', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeOmnichannel };
test.beforeAll(async ({ api, browser }) => {
await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }).then((res) => expect(res.status()).toBe(200)),
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }).then((res) => expect(res.status()).toBe(200)),
api.post('/settings/Livechat_auto_close_on_hold_chats_timeout', { value: 5 }).then((res) => expect(res.status()).toBe(200)),
api.post('/settings/Livechat_allow_manual_on_hold', { value: true }).then((res) => expect(res.status()).toBe(200)),
]);
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeChannel: new HomeOmnichannel(page) };
});
test.afterAll(async ({ api }) => {
await agent.page.close();
await Promise.all([
api.delete('/livechat/users/agent/user1').then((res) => expect(res.status()).toBe(200)),
api.post('/settings/Livechat_auto_close_on_hold_chats_timeout', { value: 3600 }).then((res) => expect(res.status()).toBe(200)),
api.post('/settings/Livechat_allow_manual_on_hold', { value: false }).then((res) => expect(res.status()).toBe(200)),
]);
});
test.beforeEach(async ({ page, api }) => {
// make "user-1" online
await agent.poHomeChannel.navbar.changeUserStatus('online');
// start a new chat for each test
newVisitor = createFakeVisitor();
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
// Note: Skipping this test as the scheduler is gonna take 1 minute to process now
// And waiting for 1 minute in a test is horrible
test.skip('expect on-hold chat to be closed automatically in 5 seconds', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
await agent.poHomeChannel.content.sendMessage('this_is_a_test_message_from_agent');
await agent.poHomeChannel.quickActionsRoomToolbar.placeChatOnHold();
await agent.poHomeChannel.content.btnModalConfirm.click();
// expect to see a system message saying the chat was on-hold
await expect(agent.poHomeChannel.content.lastSystemMessageBody).toHaveText(
`Chat On Hold: The chat was manually placed On Hold by user1`,
);
await expect(agent.poHomeChannel.composer.inputMessage).not.toBeVisible();
await expect(agent.poHomeChannel.content.resumeOnHoldOmnichannelChatButton).toBeVisible();
// current url
const chatRoomUrl = agent.page.url();
// wait for the chat to be closed automatically for 5 seconds
await agent.page.waitForTimeout(7000);
// expect to see a system message saying the chat was closed automatically in the closed chat room
await agent.page.goto(chatRoomUrl);
expect(await agent.poHomeChannel.content.lastSystemMessageBody.innerText()).toBe(
'Conversation closed: Closed automatically because chat was On Hold for 5 seconds.',
);
});
});

View File

@@ -0,0 +1,71 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeChannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe('omnichannel-auto-transfer-unanswered-chat', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent1: { page: Page; poHomeChannel: HomeChannel };
let agent2: { page: Page; poHomeChannel: HomeChannel };
test.beforeAll(async ({ api, browser }) => {
await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }).then((res) => expect(res.status()).toBe(200)),
api.post('/livechat/users/agent', { username: 'user2' }).then((res) => expect(res.status()).toBe(200)),
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }).then((res) => expect(res.status()).toBe(200)),
api.post('/settings/Livechat_auto_transfer_chat_timeout', { value: 5 }).then((res) => expect(res.status()).toBe(200)),
]);
const { page } = await createAuxContext(browser, Users.user1);
agent1 = { page, poHomeChannel: new HomeChannel(page) };
const { page: page2 } = await createAuxContext(browser, Users.user2);
agent2 = { page: page2, poHomeChannel: new HomeChannel(page2) };
});
test.afterAll(async ({ api }) => {
await agent1.page.close();
await agent2.page.close();
await Promise.all([
api.delete('/livechat/users/agent/user1').then((res) => expect(res.status()).toBe(200)),
api.delete('/livechat/users/agent/user2').then((res) => expect(res.status()).toBe(200)),
api.post('/settings/Livechat_auto_transfer_chat_timeout', { value: 0 }).then((res) => expect(res.status()).toBe(200)),
]);
});
test.beforeEach(async ({ page, api }) => {
// make "user-1" online
await agent1.poHomeChannel.navbar.switchOmnichannelStatus('online');
await agent2.poHomeChannel.navbar.switchOmnichannelStatus('offline');
// start a new chat for each test
newVisitor = createFakeVisitor();
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
test('expect chat to be auto transferred to next agent within 5 seconds of no reply from first agent', async () => {
await agent1.poHomeChannel.navbar.openChat(newVisitor.name);
await agent2.poHomeChannel.navbar.switchOmnichannelStatus('online');
// wait for the chat to be closed automatically for 5 seconds
await agent1.page.waitForTimeout(7000);
await agent2.poHomeChannel.navbar.openChat(newVisitor.name);
});
});

View File

@@ -0,0 +1,163 @@
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelBusinessHours } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent } from '../utils/omnichannel/agents';
import { createBusinessHour } from '../utils/omnichannel/businessHours';
import { createDepartment } from '../utils/omnichannel/departments';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
// TODO: These tests needs to be refactored to be independent from each other
test.describe('OC - Business Hours', () => {
test.skip(!IS_EE, 'OC - Manage Business Hours > Enterprise Edition Only');
let poOmnichannelBusinessHours: OmnichannelBusinessHours;
let department: Awaited<ReturnType<typeof createDepartment>>;
let department2: Awaited<ReturnType<typeof createDepartment>>;
let agent: Awaited<ReturnType<typeof createAgent>>;
const BHName = 'TEST Business Hours';
test.beforeAll(async ({ api }) => {
department = await createDepartment(api);
department2 = await createDepartment(api);
agent = await createAgent(api, 'user2');
expect((await setSettingValueById(api, 'Omnichannel_enable_department_removal', true)).status()).toBe(200);
await api.post('/settings/Livechat_enable_business_hours', { value: true }).then((res) => expect(res.status()).toBe(200));
await api.post('/settings/Livechat_business_hour_type', { value: 'Multiple' }).then((res) => expect(res.status()).toBe(200));
});
test.afterAll(async ({ api }) => {
await department.delete();
await department2.delete();
await agent.delete();
expect((await setSettingValueById(api, 'Omnichannel_enable_department_removal', false)).status()).toBe(200);
await api.post('/settings/Livechat_enable_business_hours', { value: false }).then((res) => expect(res.status()).toBe(200));
await api.post('/settings/Livechat_business_hour_type', { value: 'Single' }).then((res) => expect(res.status()).toBe(200));
});
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannelBusinessHours = new OmnichannelBusinessHours(page);
});
test('OC - Manage Business Hours - Create Business Hours', async () => {
await poOmnichannelBusinessHours.goto();
await test.step('expect correct form default state', async () => {
await poOmnichannelBusinessHours.btnCreateBusinessHour.click();
await poOmnichannelBusinessHours.btnBack.click();
await expect(poOmnichannelBusinessHours.inputSearch).toBeVisible();
});
await test.step('expect to create a new business hours', async () => {
await poOmnichannelBusinessHours.btnCreateBusinessHour.click();
await poOmnichannelBusinessHours.inputName.fill(BHName);
await poOmnichannelBusinessHours.selectDepartment(department.data.name);
await poOmnichannelBusinessHours.btnSave.click();
await test.step('expect business hours to have been created', async () => {
await poOmnichannelBusinessHours.search(BHName);
await expect(poOmnichannelBusinessHours.table.findRowByName(BHName)).toBeVisible();
});
});
await test.step('expect to be able to delete business hours', async () => {
await poOmnichannelBusinessHours.deleteBusinessHour(BHName);
});
await test.step('expect business hours to have been deleted', async () => {
await poOmnichannelBusinessHours.search(BHName);
await expect(poOmnichannelBusinessHours.table.findRowByName(BHName)).not.toBeVisible();
});
});
test('OC - Business hours - Edit BH departments', async ({ api }) => {
await test.step('expect to create new businessHours', async () => {
const createBH = await createBusinessHour(api, {
name: BHName,
departments: [department.data._id],
});
expect(createBH.status()).toBe(200);
});
await poOmnichannelBusinessHours.goto();
await test.step('expect to add business hours departments', async () => {
await poOmnichannelBusinessHours.search(BHName);
await poOmnichannelBusinessHours.table.findRowByName(BHName).click();
await poOmnichannelBusinessHours.selectDepartment(department2.data.name);
await poOmnichannelBusinessHours.btnSave.click();
await expect(poOmnichannelBusinessHours.btnSave).not.toBeVisible();
});
await test.step('expect department to be in the chosen departments list', async () => {
await poOmnichannelBusinessHours.search(BHName);
await poOmnichannelBusinessHours.table.findRowByName(BHName).click();
await expect(poOmnichannelBusinessHours.findDepartmentsChipOption(department2.data.name)).toBeVisible();
await poOmnichannelBusinessHours.btnBack.click();
});
await test.step('expect to remove business hours departments', async () => {
await poOmnichannelBusinessHours.search(BHName);
await poOmnichannelBusinessHours.table.findRowByName(BHName).click();
await poOmnichannelBusinessHours.selectDepartment(department2.data.name);
await poOmnichannelBusinessHours.btnSave.click();
await expect(poOmnichannelBusinessHours.btnSave).not.toBeVisible();
});
await test.step('expect department to not be in the chosen departments list', async () => {
await poOmnichannelBusinessHours.search(BHName);
await poOmnichannelBusinessHours.table.findRowByName(BHName).click();
await expect(poOmnichannelBusinessHours.findDepartmentsChipOption(department2.data.name)).toBeHidden();
await poOmnichannelBusinessHours.btnBack.click();
});
await test.step('expect delete business hours', async () => {
await poOmnichannelBusinessHours.deleteBusinessHour(BHName);
});
});
test('OC - Business hours - Toggle BH active status', async ({ api }) => {
await test.step('expect to create new businessHours', async () => {
const createBH = await createBusinessHour(api, {
name: BHName,
departments: [department.data._id],
});
expect(createBH.status()).toBe(200);
});
await poOmnichannelBusinessHours.goto();
await test.step('expect to disable business hours', async () => {
await poOmnichannelBusinessHours.search(BHName);
await poOmnichannelBusinessHours.table.findRowByName(BHName).click();
await poOmnichannelBusinessHours.getCheckboxByLabel('Enabled').click();
await expect(poOmnichannelBusinessHours.getCheckboxByLabel('Enabled')).not.toBeChecked();
await poOmnichannelBusinessHours.btnSave.click();
await expect(poOmnichannelBusinessHours.btnSave).not.toBeVisible();
});
await test.step('expect to enable business hours', async () => {
await poOmnichannelBusinessHours.search(BHName);
await poOmnichannelBusinessHours.table.findRowByName(BHName).click();
await poOmnichannelBusinessHours.getCheckboxByLabel('Enabled').click();
await expect(poOmnichannelBusinessHours.getCheckboxByLabel('Enabled')).toBeChecked();
await poOmnichannelBusinessHours.btnSave.click();
await expect(poOmnichannelBusinessHours.btnSave).not.toBeVisible();
});
await test.step('expect delete business hours', async () => {
await poOmnichannelBusinessHours.deleteBusinessHour(BHName);
});
});
});

View File

@@ -0,0 +1,93 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test } from '../utils/test';
test.describe.serial('OC - Canned Responses Sidebar', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeOmnichannel };
const cannedResponseName = faker.string.uuid();
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
// Set user user 1 as manager and agent
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeChannel: new HomeOmnichannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterAll('close livechat conversation', async () => {
await agent.poHomeChannel.quickActionsRoomToolbar.closeChat();
});
test.afterAll(async ({ api }) => {
await Promise.all([
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/manager/user1'),
poLiveChat.page.close(),
agent.page.close(),
]);
});
test('OC - Canned Responses Sidebar - Create', async () => {
await test.step('expect send a message as a visitor', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('expect to be able to open canned responses sidebar and creation', async () => {
await agent.poHomeChannel.roomToolbar.openCannedResponses();
});
await test.step('expect to create new canned response', async () => {
await agent.poHomeChannel.content.btnNewCannedResponse.click();
await agent.poHomeChannel.cannedResponses.inputShortcut.fill(cannedResponseName);
await agent.poHomeChannel.cannedResponses.inputMessage.fill(faker.lorem.paragraph());
await agent.poHomeChannel.cannedResponses.addTag(faker.commerce.department());
await agent.poHomeChannel.cannedResponses.radioPublic.click();
await agent.poHomeChannel.cannedResponses.btnSave.click();
});
});
test('OC - Canned Responses Sidebar - Edit', async () => {
await test.step('expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('expect to be able to open canned responses sidebar and creation', async () => {
await agent.poHomeChannel.roomToolbar.openCannedResponses();
});
await test.step('expect to edit canned response', async () => {
await agent.poHomeChannel.cannedResponses.listItem(cannedResponseName).click();
await agent.poHomeChannel.cannedResponses.btnEdit.click();
await agent.poHomeChannel.cannedResponses.radioPrivate.click();
await agent.poHomeChannel.cannedResponses.btnSave.click();
});
});
});

View File

@@ -0,0 +1,212 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe('OC - Canned Responses Usage', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeOmnichannel };
let cannedResponseId: string;
let placeholderResponseId: string;
let secondResponseId: string;
const cannedResponseName = `test-${faker.string.uuid()}`;
const cannedResponseText = 'Hello! Welcome to our support chat. How can I assist you today?';
const cannedResponseWithPlaceholder = `Hello {{contact.name}}, thank you for contacting us!`;
const placeholderResponseName = `placeholder-${faker.string.uuid()}`;
const secondResponseName = `followup-${faker.string.uuid()}`;
const secondResponseText = 'Is there anything else I can help you with today?';
test.beforeAll(async ({ api, browser }) => {
await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
api.post('/canned-responses', {
shortcut: cannedResponseName,
text: cannedResponseText,
scope: 'global',
tags: ['tag'],
}),
api.post('/canned-responses', {
shortcut: placeholderResponseName,
text: cannedResponseWithPlaceholder,
scope: 'global',
tags: ['tag'],
}),
api.post('/canned-responses', {
shortcut: secondResponseName,
text: secondResponseText,
scope: 'global',
tags: ['tag'],
}),
]);
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeChannel: new HomeOmnichannel(page) };
const [mainResponse, placeholderResponse, secondResponse] = await Promise.all([
api.get('/canned-responses', { shortcut: cannedResponseName }),
api.get('/canned-responses', { shortcut: placeholderResponseName }),
api.get('/canned-responses', { shortcut: secondResponseName }),
]);
const [mainData, placeholderData, secondData] = await Promise.all([
mainResponse.json(),
placeholderResponse.json(),
secondResponse.json(),
]);
cannedResponseId = mainData.cannedResponses[0]._id;
placeholderResponseId = placeholderData.cannedResponses[0]._id;
secondResponseId = secondData.cannedResponses[0]._id;
});
test.beforeEach(async ({ page, api }) => {
newVisitor = createFakeVisitor();
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterEach('close livechat conversation', async () => {
await poLiveChat.closeChat();
});
test.afterAll(async ({ api }) => {
await Promise.all([
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/manager/user1'),
api.delete('/canned-responses', { _id: cannedResponseId }),
api.delete('/canned-responses', { _id: placeholderResponseId }),
api.delete('/canned-responses', { _id: secondResponseId }),
agent.poHomeChannel.page.close(),
]);
});
test('OC - Canned Responses Usage - Create response and use in chat', async () => {
await test.step('expect to start a visitor conversation', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('expect agent to receive and open the chat', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('expect to use the canned response in the chat', async () => {
await agent.poHomeChannel.content.selectCannedResponse(cannedResponseName);
});
await test.step('expect canned response text to appear in message composer', async () => {
await expect(agent.poHomeChannel.composer.inputMessage).toHaveValue(`${cannedResponseText} `);
});
await test.step('expect agent to be able to send the canned response message', async () => {
await agent.poHomeChannel.content.sendMessage(cannedResponseText);
});
await test.step('expect visitor to receive the canned response message', async () => {
await expect(poLiveChat.txtChatMessage(cannedResponseText)).toBeVisible();
});
});
test('OC - Canned Responses Usage - Use response with placeholder replacement', async () => {
await test.step('expect to start a new visitor conversation', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.fill('test_message_for_placeholder');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('expect agent to receive and open the chat', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('expect to use the canned response with placeholder', async () => {
await agent.poHomeChannel.content.selectCannedResponse(placeholderResponseName);
});
await test.step('expect placeholder to be replaced with actual visitor name', async () => {
await agent.poHomeChannel.content.sendMessage(cannedResponseWithPlaceholder);
await expect(poLiveChat.txtChatMessage('{{contact.name}}')).not.toBeVisible();
});
});
test('OC - Canned Responses Usage - Modify response before sending', async () => {
const modifiedText = `Additionally, I'll be happy to help you with any specific questions.`;
await test.step('expect to start a visitor conversation', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.fill('test_message_for_modification');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('expect agent to open the chat', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('expect to use existing canned response and modify it', async () => {
await agent.poHomeChannel.content.selectCannedResponse(cannedResponseName);
});
await test.step('expect to modify the canned response text before sending', async () => {
await agent.poHomeChannel.composer.inputMessage.click();
await agent.page.keyboard.press('End');
await agent.poHomeChannel.composer.inputMessage.pressSequentially(modifiedText);
await expect(agent.poHomeChannel.composer.inputMessage).toHaveValue(`${cannedResponseText} ${modifiedText}`);
});
await test.step('expect to send the modified message', async () => {
await agent.page.keyboard.press('Enter');
});
await test.step('expect visitor to receive the modified message', async () => {
await expect(poLiveChat.txtChatMessage(`${cannedResponseText} ${modifiedText}`)).toBeVisible();
});
});
test('OC - Canned Responses Usage - Use multiple responses in sequence', async () => {
await test.step('expect to start a visitor conversation', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.fill('test_message_for_sequence');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('expect agent to open the chat', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('expect to use first canned response', async () => {
await agent.poHomeChannel.content.selectCannedResponse(cannedResponseName);
await expect(agent.poHomeChannel.composer.inputMessage).toHaveValue(`${cannedResponseText} `);
await agent.page.keyboard.press('Enter');
});
await test.step('expect to use second canned response', async () => {
await agent.poHomeChannel.content.selectCannedResponse(secondResponseName);
await expect(agent.poHomeChannel.composer.inputMessage).toHaveValue(`${secondResponseText} `);
await agent.page.keyboard.press('Enter');
});
await test.step('expect visitor to receive both messages in correct order', async () => {
await expect(poLiveChat.txtChatMessage(cannedResponseText)).toBeVisible();
await expect(poLiveChat.txtChatMessage(secondResponseText)).toBeVisible();
});
});
});

View File

@@ -0,0 +1,106 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { ADMIN_CREDENTIALS, IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeChannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { getPriorityByi18nLabel } from '../utils/omnichannel/priority';
import { createSLA } from '../utils/omnichannel/sla';
import { test, expect } from '../utils/test';
const getRoomId = (page: Page): string => {
// url is of the form: http://localhost:3000/live/:rid/room-info
const url = page?.url();
// rid comes after /live/ and before /room-info (or /)
const rid = url?.split('/live/')[1].split('/')[0];
if (!rid) {
throw new Error(`Could not get room id from url: ${page.url()}`);
}
return rid;
};
test.describe.serial('omnichannel-changing-room-priority-and-sla', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeChannel };
test.beforeAll(async ({ api, browser }) => {
let statusCode = (await api.post('/livechat/users/agent', { username: ADMIN_CREDENTIALS.username })).status();
expect(statusCode).toBe(200);
statusCode = (await api.post('/livechat/users/manager', { username: ADMIN_CREDENTIALS.username })).status();
expect(statusCode).toBe(200);
statusCode = (await api.post('/livechat/agent.status', { status: 'available' })).status();
expect(statusCode).toBe(200);
statusCode = (await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' })).status();
expect(statusCode).toBe(200);
const { page } = await createAuxContext(browser, Users.admin);
agent = { page, poHomeChannel: new HomeChannel(page) };
await agent.poHomeChannel.navbar.changeUserStatus('online');
});
test.afterAll(async ({ api }) => {
await agent.page.close();
await Promise.all([
api.delete(`/livechat/users/agent/${ADMIN_CREDENTIALS.username}`),
api.delete(`/livechat/users/manager/${ADMIN_CREDENTIALS.username}`),
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }),
]);
});
test('expect to initiate a new livechat conversation', async ({ page, api }) => {
newVisitor = createFakeVisitor();
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await agent.poHomeChannel.sidebar.getSidebarItemByName(newVisitor.name).click();
});
test('expect to change priority of room and corresponding system message should be displayed', async ({ api }) => {
const priority = await getPriorityByi18nLabel(api, 'High');
await test.step('change priority of room to the new priority', async () => {
const status = (await api.post(`/livechat/room/${getRoomId(agent.page)}/priority`, { priorityId: priority._id })).status();
await expect(status).toBe(200);
await agent.page.waitForTimeout(1000);
});
await expect(agent.poHomeChannel.content.lastSystemMessageBody).toHaveText(
`Priority changed: ${ADMIN_CREDENTIALS.username} changed the priority to ${priority.name || priority.i18n}`,
);
});
test('expect to change SLA of room and corresponding system message should be displayed', async ({ api }) => {
const sla = await createSLA(api);
await test.step('change SLA of room to the new SLA', async () => {
const status = (await api.put(`/livechat/inquiry.setSLA`, { sla: sla.name, roomId: getRoomId(agent.page) })).status();
expect(status).toBe(200);
await agent.page.waitForTimeout(1000);
});
await expect(agent.poHomeChannel.content.lastSystemMessageBody).toHaveText(
`SLA Policy changed: ${ADMIN_CREDENTIALS.username} changed the SLA Policy to ${sla.name}`,
);
await test.step('cleanup SLA', async () => {
const status = (await api.delete(`/livechat/sla/${sla._id}`)).status();
expect(status).toBe(200);
});
});
});

View File

@@ -0,0 +1,98 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe('Omnichannel chat history', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
// Set user user 1 as manager and agent
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeOmnichannel: new HomeOmnichannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterAll(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await api.delete('/livechat/users/manager/user1');
await agent.page.close();
await api.post('/permissions.update', { permissions: [{ _id: 'preview-c-room', roles: ['admin', 'owner', 'moderator', 'user'] }] });
});
test('Receiving a message from visitor', async ({ api }) => {
await test.step('Expect send a message as a visitor', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeOmnichannel.navbar.openChat(newVisitor.name);
});
await test.step('expect to be able to edit room info', async () => {
await agent.poHomeOmnichannel.roomInfo.btnEdit.click();
await agent.poHomeOmnichannel.editRoomInfo.inputTopic.fill('any_topic');
await agent.poHomeOmnichannel.editRoomInfo.btnSave.click();
await expect(agent.poHomeOmnichannel.roomInfo.root).toContainText('any_topic');
});
await test.step('Expect to be able to close an omnichannel to conversation', async () => {
await agent.poHomeOmnichannel.quickActionsRoomToolbar.closeChat();
});
await test.step('Expect send a message as a visitor again to reopen chat', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeOmnichannel.navbar.openChat(newVisitor.name);
});
await test.step('Expect to be able to see conversation history', async () => {
await agent.poHomeOmnichannel.btnContactInfo.click();
await agent.poHomeOmnichannel.contacts.contactInfo.tabHistory.click();
await expect(agent.poHomeOmnichannel.contacts.contactInfo.historyItem).toBeVisible();
await agent.poHomeOmnichannel.contacts.contactInfo.historyItem.click();
await expect(agent.poHomeOmnichannel.contacts.contactInfo.historyMessage).toBeVisible();
});
await api.post('/permissions.update', { permissions: [{ _id: 'preview-c-room', roles: [] }] });
await test.step('Expect agent to see conversation history, but not join room', async () => {
await agent.page.reload();
await agent.poHomeOmnichannel.contacts.contactInfo.historyItem.click();
await agent.poHomeOmnichannel.contacts.contactInfo.historyMessage.click();
await agent.poHomeOmnichannel.contacts.contactInfo.btnOpenChat.click();
// Should not show the NoSubscribedRoom.tsx component on livechat rooms
await expect(agent.page.locator('div >> text=This conversation is already closed.')).toBeVisible();
await expect(agent.page.locator('div >> text="this_a_test_message_from_visitor"')).toBeVisible();
});
});
});

View File

@@ -0,0 +1,512 @@
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { setSettingValueById } from '../utils';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments';
import { createManager } from '../utils/omnichannel/managers';
import { createMonitor } from '../utils/omnichannel/monitors';
import { createConversation } from '../utils/omnichannel/rooms';
import { createOrUpdateUnit } from '../utils/omnichannel/units';
import { expect, test } from '../utils/test';
const wrapSession = async ({ page }: { page: Page }) => ({ page, poHomeOmnichannel: new HomeOmnichannel(page) });
test.use({ storageState: Users.user3.state });
test.skip(!IS_EE, 'Enterprise Edition Only');
test.describe('OC - Chat transfers [Monitor role]', () => {
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let conversations: Awaited<ReturnType<typeof createConversation>>[];
let agents: Awaited<ReturnType<typeof createAgent>>[];
let monitors: Awaited<ReturnType<typeof createMonitor>>[];
let units: Awaited<ReturnType<typeof createOrUpdateUnit>>[];
let sessions: { page: Page; poHomeOmnichannel: HomeOmnichannel }[] = [];
let poOmnichannel: HomeOmnichannel;
// Create agents
test.beforeAll(async ({ api }) => {
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2'), createAgent(api, 'rocketchat.internal.admin.test')]);
(await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)))).forEach((res) => {
expect(res.status()).toBe(200);
});
});
// Create departments
test.beforeAll(async ({ api }) => {
departments = await Promise.all([createDepartment(api), createDepartment(api)]);
});
// Add agents to departments
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
const promises = await Promise.all([
addAgentToDepartment(api, { department: departmentA, agentId: 'user1' }),
addAgentToDepartment(api, { department: departmentA, agentId: 'rocketchat.internal.admin.test' }),
addAgentToDepartment(api, { department: departmentB, agentId: 'user2' }),
]);
promises.forEach((res) => expect(res.status()).toBe(200));
});
// Create conversations
test.beforeAll(async ({ api }) => {
const [departmentA] = departments.map(({ data }) => data);
conversations = await Promise.all([
createConversation(api, {
agentId: `user1`,
departmentId: departmentA._id,
}),
createConversation(api, {
agentId: `user1`,
departmentId: departmentA._id,
}),
createConversation(api, {
agentId: `user1`,
departmentId: departmentA._id,
}),
]);
});
// Create monitors
test.beforeAll(async ({ api }) => {
monitors = await Promise.all([createMonitor(api, 'user3')]);
});
// Create units
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
units = await Promise.all([
createOrUpdateUnit(api, {
monitors: [{ monitorId: 'user3', username: 'user3' }],
departments: [{ departmentId: departmentA._id }],
}),
createOrUpdateUnit(api, {
monitors: [{ monitorId: 'rocketchat.internal.admin.test', username: 'rocketchat.internal.admin.test' }],
departments: [{ departmentId: departmentB._id }],
}),
]);
expect((await setSettingValueById(api, 'Omnichannel_enable_department_removal', true)).status()).toBe(200);
});
// Create sessions
test.beforeEach(async ({ browser }) => {
sessions = [];
for (const user of [Users.user1, Users.user2, Users.admin]) {
sessions.push(await createAuxContext(browser, user).then(wrapSession));
}
});
test.beforeEach(async ({ page }) => {
poOmnichannel = new HomeOmnichannel(page);
await poOmnichannel.chats.goto();
});
// Close sessions
test.afterEach(async () => {
await Promise.all(sessions.map(({ page }) => page.close()));
sessions = [];
});
test.afterAll(async ({ api }) => {
await Promise.all([
...conversations.map((conversation) => conversation.delete()),
...monitors.map((monitor) => monitor.delete()),
...agents.map((agent) => agent.delete()),
...units.map((unit) => unit.delete()),
...departments.map((department) => department.delete()),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', false);
});
test(`OC - Chat transfers [Monitor role] - Transfer to department with no online agents should fail`, async ({ api }) => {
const [roomA] = conversations.map(({ data }) => data.room);
const [, agentB] = sessions;
const emptyDepartment = await createDepartment(api);
await test.step('expect to open forward chat modal', async () => {
await poOmnichannel.chats.openChat(roomA.fname);
await poOmnichannel.quickActionsRoomToolbar.forwardChat();
});
await test.step('expect transfer to department with no online agents to fail', async () => {
await poOmnichannel.content.forwardChatModal.selectDepartment(emptyDepartment.data.name);
await poOmnichannel.content.forwardChatModal.inputComment.type('transfer_attempt');
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled();
await poOmnichannel.content.forwardChatModal.btnForward.click();
await poOmnichannel.toastMessage.waitForDisplay({ type: 'error' });
});
await test.step('expect conversation to remain with original agent', async () => {
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname)).not.toBeVisible();
});
await emptyDepartment.delete();
});
test(`OC - Chat transfers [Monitor role] - Transfer to another department`, async () => {
const [, departmentB] = departments.map(({ data }) => data);
const [roomA] = conversations.map(({ data }) => data.room);
const [agentA, agentB] = sessions;
await test.step('expect room a to bot be visible for user2', async () => {
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname)).not.toBeVisible();
});
await test.step('expect to be able to join chats from same unit', async () => {
await poOmnichannel.chats.openChat(roomA.fname);
await poOmnichannel.quickActionsRoomToolbar.forwardChat();
});
await test.step('expect agent and department fields to be visible and enabled', async () => {
await expect(poOmnichannel.content.forwardChatModal.inputForwardUser).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.inputForwardDepartment).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled();
});
await test.step('expect to transfer from dep a to dep b', async () => {
await poOmnichannel.content.forwardChatModal.selectDepartment(departmentB.name);
await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment');
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled();
await poOmnichannel.content.forwardChatModal.btnForward.click();
await poOmnichannel.content.forwardChatModal.waitForDismissal();
// await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible();
});
await test.step('expect conversation to have been assigned to user 2', async () => {
await expect(agentA.page).toHaveURL(`/home`);
await expect(agentA.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname)).not.toBeVisible();
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname)).toBeVisible();
});
await test.step('expect user 1 to have left the conversation', async () => {
await agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname).click();
await agentB.poHomeOmnichannel.content.findSystemMessage(
`Transfer: user3 transferred the chat to the department ${departmentB.name}}`,
);
await agentB.poHomeOmnichannel.content.findSystemMessage('left the channel');
});
});
test(`OC - Chat transfers [Monitor role] - Transfer to another agent, different department`, async () => {
const [, roomB] = conversations.map(({ data }) => data.room);
const [agentA, agentB] = sessions;
await test.step('expect room a to bot be visible for user2', async () => {
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomB.fname)).not.toBeVisible();
});
await test.step('expect to be able to join chats from same unit', async () => {
await poOmnichannel.chats.openChat(roomB.fname);
await poOmnichannel.quickActionsRoomToolbar.forwardChat();
});
await test.step('expect agent and department fields to be visible and enabled', async () => {
await expect(poOmnichannel.content.forwardChatModal.inputForwardUser).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.inputForwardDepartment).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled();
});
await test.step('expect to transfer from user1 to user2', async () => {
await poOmnichannel.content.forwardChatModal.selectUser(`user2`);
await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment');
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled();
await poOmnichannel.content.forwardChatModal.btnForward.click();
// await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible();
});
await test.step('expect conversation to have been assigned to user 2', async () => {
await expect(agentA.page).toHaveURL(`/home`);
await expect(agentA.poHomeOmnichannel.sidebar.getSidebarItemByName(roomB.fname)).not.toBeVisible();
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomB.fname)).toBeVisible();
});
await test.step('expect user 1 to have left the conversation', async () => {
await agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomB.fname).click();
await expect(
agentB.poHomeOmnichannel.content.findSystemMessage(
`New Chat Transfer: user3 transferred the chat to user2 with a comment: any_comment`,
),
).toBeVisible();
await expect(agentB.poHomeOmnichannel.content.findSystemMessage('left the channel')).toBeVisible();
});
});
test(`OC - Chat transfers [Monitor role] - Transfer to another agent, same department`, async () => {
const [, , roomC] = conversations.map(({ data }) => data.room);
const [agentA, , agentC] = sessions;
await test.step('expect room a to bot be visible for user2', async () => {
await expect(agentC.poHomeOmnichannel.sidebar.getSidebarItemByName(roomC.fname)).not.toBeVisible();
});
await test.step('expect to be able to join chats from same unit', async () => {
await poOmnichannel.chats.openChat(roomC.fname);
await poOmnichannel.quickActionsRoomToolbar.forwardChat();
});
await test.step('expect agent and department fields to be visible and enabled', async () => {
await expect(poOmnichannel.content.forwardChatModal.inputForwardUser).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.inputForwardDepartment).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled();
});
await test.step('expect to transfer from dep a to dep b', async () => {
await poOmnichannel.content.forwardChatModal.selectUser('rocketchat.internal.admin.test');
await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment');
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled();
await poOmnichannel.content.forwardChatModal.btnForward.click();
// await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible();
});
await test.step('expect conversation to have been assigned to user 2', async () => {
await expect(agentA.page).toHaveURL(`/home`);
await expect(agentA.poHomeOmnichannel.sidebar.getSidebarItemByName(roomC.fname)).not.toBeVisible();
await expect(agentC.poHomeOmnichannel.sidebar.getSidebarItemByName(roomC.fname)).toBeVisible();
});
await test.step('expect user 1 to have left the conversation', async () => {
await agentC.poHomeOmnichannel.sidebar.getSidebarItemByName(roomC.fname).click();
await expect(
agentC.poHomeOmnichannel.content.findSystemMessage(
`New Chat Transfer: user3 transferred the chat to RocketChat Internal Admin Test with a comment: any_comment`,
),
).toBeVisible();
await expect(agentC.poHomeOmnichannel.content.findSystemMessage('left the channel')).toBeVisible();
});
});
});
test.describe('OC - Chat transfers [Manager role]', () => {
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let conversations: Awaited<ReturnType<typeof createConversation>>[];
let agents: Awaited<ReturnType<typeof createAgent>>[];
let managers: Awaited<ReturnType<typeof createManager>>[];
let sessions: { page: Page; poHomeOmnichannel: HomeOmnichannel }[];
let poOmnichannel: HomeOmnichannel;
// Create agents
test.beforeAll(async ({ api }) => {
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2'), createAgent(api, 'rocketchat.internal.admin.test')]);
(await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)))).forEach((res) => {
expect(res.status()).toBe(200);
});
});
// Create managers
test.beforeAll(async ({ api }) => {
managers = await Promise.all([createManager(api, 'user3')]);
});
// Create departments
test.beforeAll(async ({ api }) => {
departments = await Promise.all([createDepartment(api), createDepartment(api)]);
});
// Add agents to departments
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
const promises = await Promise.all([
addAgentToDepartment(api, { department: departmentA, agentId: 'user1' }),
addAgentToDepartment(api, { department: departmentA, agentId: 'rocketchat.internal.admin.test' }),
addAgentToDepartment(api, { department: departmentB, agentId: 'user2' }),
]);
promises.forEach((res) => expect(res.status()).toBe(200));
});
// Create conversations
test.beforeAll(async ({ api }) => {
const [departmentA] = departments.map(({ data }) => data);
conversations = await Promise.all([
createConversation(api, {
agentId: `user1`,
departmentId: departmentA._id,
}),
createConversation(api, {
agentId: `user1`,
departmentId: departmentA._id,
}),
createConversation(api, {
agentId: `user1`,
departmentId: departmentA._id,
}),
]);
});
// Create sessions
test.beforeEach(async ({ browser }) => {
sessions = await Promise.all([
createAuxContext(browser, Users.user1).then(wrapSession),
createAuxContext(browser, Users.user2).then(wrapSession),
createAuxContext(browser, Users.admin).then(wrapSession),
]);
});
test.beforeEach(async ({ page }) => {
poOmnichannel = new HomeOmnichannel(page);
await poOmnichannel.chats.goto();
});
// Close sessions
test.afterEach(async () => {
await Promise.all(sessions.map(({ page }) => page.close()));
});
test.afterAll(async () => {
await Promise.all([
...conversations.map((conversation) => conversation.delete()),
...managers.map((manager) => manager.delete()),
...agents.map((agent) => agent.delete()),
...departments.map((department) => department.delete()),
]);
});
test(`OC - Chat transfers [Manager role] - Transfer to another department`, async () => {
const [, departmentB] = departments.map(({ data }) => data);
const [roomA] = conversations.map(({ data }) => data.room);
const [agentA, agentB] = sessions;
await test.step('expect room a to bot be visible for user2', async () => {
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname)).not.toBeVisible();
});
await test.step('expect to be able to join chats from same unit', async () => {
await poOmnichannel.chats.openChat(roomA.fname);
await poOmnichannel.quickActionsRoomToolbar.forwardChat();
});
await test.step('expect agent and department fields to be visible and enabled', async () => {
await expect(poOmnichannel.content.forwardChatModal.inputForwardUser).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.inputForwardDepartment).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled();
});
await test.step('expect to transfer from dep a to dep b', async () => {
await poOmnichannel.content.forwardChatModal.selectDepartment(departmentB.name);
await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment');
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled();
await poOmnichannel.content.forwardChatModal.btnForward.click();
await poOmnichannel.content.forwardChatModal.waitForDismissal();
// await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible();
});
await test.step('expect conversation to have been assigned to user 2', async () => {
await expect(agentA.page).toHaveURL(`/home`);
await expect(agentA.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname)).not.toBeVisible();
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname)).toBeVisible();
});
await test.step('expect user 1 to have left the conversation', async () => {
await agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomA.fname).click();
await agentB.poHomeOmnichannel.content.findSystemMessage(
`Transfer: user3 transferred the chat to the department ${departmentB.name}}`,
);
await agentB.poHomeOmnichannel.content.findSystemMessage('left the channel');
});
});
test(`OC - Chat transfers [Manager role] - Transfer to another agent, different department`, async () => {
const [, roomB] = conversations.map(({ data }) => data.room);
const [agentA, agentB] = sessions;
await test.step('expect room a to bot be visible for user2', async () => {
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomB.fname)).not.toBeVisible();
});
await test.step('expect to be able to join chats from same unit', async () => {
await poOmnichannel.chats.openChat(roomB.fname);
await poOmnichannel.quickActionsRoomToolbar.forwardChat();
});
await test.step('expect agent and department fields to be visible and enabled', async () => {
await expect(poOmnichannel.content.forwardChatModal.inputForwardUser).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.inputForwardDepartment).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled();
});
await test.step('expect to transfer from user1 to user2', async () => {
await poOmnichannel.content.forwardChatModal.selectUser(`user2`);
await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment');
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled();
await poOmnichannel.content.forwardChatModal.btnForward.click();
// await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible();
});
await test.step('expect conversation to have been assigned to user 2', async () => {
await expect(agentA.page).toHaveURL(`/home`);
await expect(agentA.poHomeOmnichannel.sidebar.getSidebarItemByName(roomB.fname)).not.toBeVisible();
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomB.fname)).toBeVisible();
});
await test.step('expect user 1 to have left the conversation', async () => {
await agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(roomB.fname).click();
await expect(
agentB.poHomeOmnichannel.content.findSystemMessage(
`New Chat Transfer: user3 transferred the chat to user2 with a comment: any_comment`,
),
).toBeVisible();
await expect(agentB.poHomeOmnichannel.content.findSystemMessage('left the channel')).toBeVisible();
});
});
test(`OC - Chat transfers [Manager role] - Transfer to another agent, same department`, async () => {
const [, , roomC] = conversations.map(({ data }) => data.room);
const [agentA, , agentC] = sessions;
await test.step('expect room a to bot be visible for user2', async () => {
await expect(agentC.poHomeOmnichannel.sidebar.getSidebarItemByName(roomC.fname)).not.toBeVisible();
});
await test.step('expect to be able to join chats from same unit', async () => {
await poOmnichannel.chats.openChat(roomC.fname);
await poOmnichannel.quickActionsRoomToolbar.forwardChat();
});
await test.step('expect agent and department fields to be visible and enabled', async () => {
await expect(poOmnichannel.content.forwardChatModal.inputForwardUser).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.inputForwardDepartment).toBeEnabled();
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeDisabled();
});
await test.step('expect to transfer from dep a to dep b', async () => {
await poOmnichannel.content.forwardChatModal.selectUser('rocketchat.internal.admin.test');
await poOmnichannel.content.forwardChatModal.inputComment.type('any_comment');
await expect(poOmnichannel.content.forwardChatModal.btnForward).toBeEnabled();
await poOmnichannel.content.forwardChatModal.btnForward.click();
// await expect(agentA.poHomeOmnichannel.toastSuccess).toBeVisible();
});
await test.step('expect conversation to have been assigned to user 2', async () => {
await expect(agentA.page).toHaveURL(`/home`);
await expect(agentA.poHomeOmnichannel.sidebar.getSidebarItemByName(roomC.fname)).not.toBeVisible();
await expect(agentC.poHomeOmnichannel.sidebar.getSidebarItemByName(roomC.fname)).toBeVisible();
});
await test.step('expect user 1 to have left the conversation', async () => {
await agentC.poHomeOmnichannel.sidebar.getSidebarItemByName(roomC.fname).click();
await expect(
agentC.poHomeOmnichannel.content.findSystemMessage(
`New Chat Transfer: user3 transferred the chat to RocketChat Internal Admin Test with a comment: any_comment`,
),
).toBeVisible();
await expect(agentC.poHomeOmnichannel.content.findSystemMessage('left the channel')).toBeVisible();
});
});
});

View File

@@ -0,0 +1,53 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test } from '../utils/test';
test.describe('Omnichannel close chat', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
// Set user user 1 as manager and agent
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeOmnichannel: new HomeOmnichannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterAll(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await api.delete('/livechat/users/manager/user1');
await agent.page.close();
});
test('Receiving a message from visitor', async () => {
await test.step('Expect send a message as a visitor', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeOmnichannel.navbar.openChat(newVisitor.name);
});
await test.step('Expect to be able to close an omnichannel to conversation', async () => {
await agent.poHomeOmnichannel.quickActionsRoomToolbar.closeChat();
});
});
});

View File

@@ -0,0 +1,67 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe('Omnichannel close inquiry', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeAll(async ({ api }) => {
newVisitor = createFakeVisitor();
await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }).then((res) => expect(res.status()).toBe(200));
await api.post('/livechat/users/manager', { username: 'user1' });
await api.post('/livechat/users/agent', { username: 'user1' });
});
test.beforeEach(async ({ page, api, browser }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
const { page: auxPage } = await createAuxContext(browser, Users.user1);
agent = { page: auxPage, poHomeOmnichannel: new HomeOmnichannel(auxPage) };
});
test.afterEach(async () => {
await agent.page.close();
});
test.afterAll(async ({ api }) => {
await Promise.all([
await api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }).then((res) => expect(res.status()).toBe(200)),
await api.delete('/livechat/users/agent/user1'),
await api.delete('/livechat/users/manager/user1'),
]);
});
test('Receiving a message from visitor', async () => {
await test.step('Expect send a message as a visitor', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeOmnichannel.sidebar.getSidebarItemByName(newVisitor.name).click();
await expect(agent.poHomeOmnichannel.content.btnTakeChat).toBeVisible();
});
await test.step('Expect to be able to close an inquiry conversation', async () => {
await agent.poHomeOmnichannel.quickActionsRoomToolbar.closeChat();
});
await test.step('Expect to inquiry be closed when navigate back', async () => {
await agent.poHomeOmnichannel.chats.goto();
await agent.poHomeOmnichannel.chats.openChat(newVisitor.name);
await expect(agent.poHomeOmnichannel.content.btnTakeChat).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,77 @@
import { faker } from '@faker-js/faker/locale/af_ZA';
import { Users } from '../fixtures/userStates';
import { OmnichannelContactCenterChats } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.user1.state });
test.describe('OC - Contact Center - Chats', () => {
let conversations: Awaited<ReturnType<typeof createConversation>>[];
let agent: Awaited<ReturnType<typeof createAgent>>;
let poOmniChats: OmnichannelContactCenterChats;
const uuid = faker.string.uuid();
const visitorA = `visitorA_${uuid}`;
const visitorB = `visitorB_${uuid}`;
test.beforeAll(async ({ api }) => {
expect((await setSettingValueById(api, 'Livechat_Routing_Method', 'Auto_Selection')).status()).toBe(200);
});
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
expect((await makeAgentAvailable(api, agent.data._id)).status()).toBe(200);
});
test.beforeAll(async ({ api }) => {
conversations = await Promise.all([
createConversation(api, { agentId: `user1`, visitorName: visitorA }),
createConversation(api, { agentId: `user1`, visitorName: visitorB }),
]);
});
test.beforeEach(async ({ page }) => {
poOmniChats = new OmnichannelContactCenterChats(page);
await poOmniChats.goto();
});
test.afterEach(async ({ page }) => {
await page.close();
});
test.afterAll(async ({ api }) => {
await Promise.all([...conversations.map((conversation) => conversation.delete()), agent.delete()]);
expect((await setSettingValueById(api, 'Livechat_Routing_Method', 'Auto_Selection')).status()).toBe(200);
});
test(`OC - Contact Center - Chats - Filter from and to same date`, async ({ page }) => {
await test.step('expect conversations to be visible', async () => {
await poOmniChats.inputSearch.fill(uuid);
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
});
await test.step('expect to filter [from] and [to] today', async () => {
const [chatA] = conversations.map((c) => c.data);
const [todayString] = new Date(chatA.room.ts).toISOString().split('T');
await poOmniChats.btnFilters.click();
await expect(page).toHaveURL('/omnichannel/current/chats/filters');
await poOmniChats.filters.inputFrom.fill(todayString);
await poOmniChats.filters.inputTo.fill(todayString);
await poOmniChats.filters.btnApply.click();
await page.waitForResponse('**/api/v1/livechat/rooms*');
});
await test.step('expect conversations to be visible', async () => {
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
});
});
});

View File

@@ -0,0 +1,227 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelContactCenterChats } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments';
import { createConversation, updateRoom } from '../utils/omnichannel/rooms';
import { createTag } from '../utils/omnichannel/tags';
import { test, expect } from '../utils/test';
const visitorA = `${faker.person.firstName()}-${faker.database.mongodbObjectId()}`;
const visitorB = `${faker.person.firstName()}-${faker.database.mongodbObjectId()}`;
const visitorC = `${faker.person.firstName()}-${faker.database.mongodbObjectId()}`;
test.skip(!IS_EE, 'OC - Contact Center Chats > Enterprise Only');
test.use({ storageState: Users.admin.state });
test.describe('OC - Contact Center Chats [Auto Selection]', async () => {
let poContactCenterChats: OmnichannelContactCenterChats;
let poHomeOmnichannel: HomeOmnichannel;
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let conversations: Awaited<ReturnType<typeof createConversation>>[];
let agents: Awaited<ReturnType<typeof createAgent>>[];
let tagA: Awaited<ReturnType<typeof createTag>>;
let tagB: Awaited<ReturnType<typeof createTag>>;
// Allow manual on hold
test.beforeAll(async ({ api }) => {
const responses = await Promise.all([
api.post('/settings/Livechat_allow_manual_on_hold', { value: true }),
api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: false }),
]);
responses.forEach((res) => expect(res.status()).toBe(200));
});
// Create departments
test.beforeAll(async ({ api }) => {
departments = await Promise.all([createDepartment(api), createDepartment(api)]);
});
// Create agents
test.beforeAll(async ({ api }) => {
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]);
const agentsStatuses = await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)));
agentsStatuses.forEach((res) => expect(res.status()).toBe(200));
});
// Add agents to departments
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
const promises = await Promise.all([
addAgentToDepartment(api, { department: departmentA, agentId: 'user1' }),
addAgentToDepartment(api, { department: departmentB, agentId: 'user2' }),
]);
promises.forEach((res) => expect(res.status()).toBe(200));
});
// Create tags
test.beforeAll(async ({ api }) => {
tagA = await createTag(api);
tagB = await createTag(api);
[tagA, tagB].forEach((res) => expect(res.response.status()).toBe(200));
});
// Create rooms
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
conversations = await Promise.all([
createConversation(api, {
visitorName: visitorA,
visitorToken: visitorA,
agentId: `user1`,
departmentId: departmentA._id,
}),
createConversation(api, {
visitorName: visitorB,
visitorToken: visitorB,
agentId: `user2`,
departmentId: departmentB._id,
}),
createConversation(api, {
visitorName: visitorC,
visitorToken: visitorC,
}),
]);
const [conversationA, conversationB] = conversations.map(({ data }) => data);
await Promise.all([
updateRoom(api, {
roomId: conversationA.room._id,
visitorId: conversationA.visitor._id,
tags: [tagA.data.name],
}),
updateRoom(api, {
roomId: conversationB.room._id,
visitorId: conversationB.visitor._id,
tags: [tagB.data.name],
}),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', true);
});
test.beforeEach(async ({ page }: { page: Page }) => {
poContactCenterChats = new OmnichannelContactCenterChats(page);
poHomeOmnichannel = new HomeOmnichannel(page);
await poContactCenterChats.goto();
});
test.afterAll(async ({ api }) => {
await Promise.all([
// Delete conversations
...conversations.map((conversation) => conversation.delete()),
// Delete departments
...departments.map((department) => department.delete()),
// Delete agents
...agents.map((agent) => agent.delete()),
// Delete tags
...[tagA, tagB].map((tag) => tag.delete()),
// Reset setting
api.post('/settings/Livechat_allow_manual_on_hold', { value: false }),
api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: true }),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', false);
});
// Change conversation A to on hold and close conversation B
test.beforeAll(async ({ api }) => {
const [conversationA, , conversationC] = conversations.map(({ data }) => data);
const statesPromises = await Promise.all([
api.post('/livechat/room.onHold', { roomId: conversationA.room._id }),
api.post('/livechat/room.close', { rid: conversationC.room._id, token: visitorC }),
]);
statesPromises.forEach((res) => expect(res.status()).toBe(200));
});
test('OC - Contact Center Chats - Basic navigation', async ({ page }) => {
await test.step('expect to be return using return button', async () => {
await poContactCenterChats.openChat(visitorA);
await poHomeOmnichannel.content.btnReturn.click();
expect(page.url()).toContain(`/omnichannel/current`);
});
});
test('OC - Contact Center Chats - Access in progress conversation from another agent', async () => {
await test.step('expect to be able to join', async () => {
const { visitor: visitorB } = conversations[1].data;
await poContactCenterChats.openChat(visitorB.name);
await expect(poHomeOmnichannel.composer.btnJoinRoom).toBeVisible();
await poHomeOmnichannel.composer.btnJoinRoom.click();
await expect(poHomeOmnichannel.composer.btnJoinRoom).not.toBeVisible();
});
});
test('OC - Contact Center Chats - Remove conversations', async () => {
await test.step('expect to be able to remove conversation from table', async () => {
await poContactCenterChats.removeChatByName(visitorC);
await expect(poContactCenterChats.table.findRowByName(visitorC)).not.toBeVisible();
});
// TODO: await test.step('expect to be able to close all closes conversations', async () => {});
});
});
test.describe('OC - Contact Center [Manual Selection]', () => {
let queuedConversation: Awaited<ReturnType<typeof createConversation>>;
let poHomeOmnichannel: HomeOmnichannel;
let poCurrentChats: OmnichannelContactCenterChats;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ api }) => {
const res = await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' });
expect(res.status()).toBe(200);
});
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'rocketchat.internal.admin.test');
const agentStatus = await makeAgentAvailable(api, agent.data._id);
expect(agentStatus.status()).toBe(200);
});
test.beforeEach(async ({ page }: { page: Page }) => {
poHomeOmnichannel = new HomeOmnichannel(page);
poCurrentChats = new OmnichannelContactCenterChats(page);
await poCurrentChats.goto();
});
test('OC - Contact Center Chats - Access queued conversation', async ({ api }) => {
queuedConversation = await createConversation(api, { visitorToken: 'visitorQueued' });
await test.step('expect to be able to take it', async () => {
const { visitor } = queuedConversation.data;
await poCurrentChats.inputSearch.fill(visitor.name);
await poCurrentChats.openChat(visitor.name);
await expect(poHomeOmnichannel.content.btnTakeChat).toBeVisible();
await poHomeOmnichannel.content.btnTakeChat.click();
await expect(poHomeOmnichannel.content.btnTakeChat).not.toBeVisible();
});
});
test.afterAll(async ({ api }) => {
const res = await api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' });
expect(res.status()).toBe(200);
});
test.afterAll(async () => {
await queuedConversation.delete();
await agent.delete();
});
});

View File

@@ -0,0 +1,287 @@
import { randomBytes } from 'crypto';
import { faker } from '@faker-js/faker';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { Navbar } from '../page-objects/fragments';
import { OmnichannelContactCenterContacts } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
const createToken = (): string => {
const array = new Uint8Array(16);
const buffer = randomBytes(16);
array.set(buffer);
return Array.from(array)
.map((byte) => byte.toString(16).padStart(2, '0'))
.join('');
};
const createContact = (generateToken = false) => ({
id: null,
name: `${faker.person.firstName()} ${faker.person.lastName()}`,
email: faker.internet.email().toLowerCase(),
phone: faker.phone.number('+############'),
token: generateToken ? createToken() : null,
customFields: {},
});
const NEW_CONTACT = createContact();
const EDIT_CONTACT = createContact();
const EXISTING_CONTACT = {
id: undefined,
name: `${faker.person.firstName()} ${faker.person.lastName()}`,
emails: [faker.internet.email().toLowerCase()],
phones: [faker.phone.number('+############')],
token: undefined,
};
const DELETE_CONTACT = {
id: undefined,
name: `${faker.person.firstName()} ${faker.person.lastName()}`,
emails: [faker.internet.email().toLowerCase()],
phones: [faker.phone.number('+############')],
};
const URL = {
contactCenter: '/omnichannel-directory/contacts',
newContact: '/omnichannel-directory/contacts/new',
get editContact() {
return `${this.contactCenter}/edit/${NEW_CONTACT.id}`;
},
get contactInfo() {
return `${this.contactCenter}/details/${NEW_CONTACT.id}`;
},
};
const ERROR = {
nameRequired: 'Name required',
invalidEmail: 'Invalid email address',
emailRequired: 'Email required',
emailAlreadyExists: 'Email already exists',
phoneRequired: 'Phone required',
phoneAlreadyExists: 'Phone already exists',
};
test.use({ storageState: Users.admin.state });
test.describe('OC - Contact Center - Contacts', () => {
let poContacts: OmnichannelContactCenterContacts;
let poNavbar: Navbar;
test.beforeAll(async ({ api }) => {
// Add contacts
await Promise.all([api.post('/omnichannel/contacts', EXISTING_CONTACT), api.post('/omnichannel/contacts', DELETE_CONTACT)]);
});
test.afterAll(async ({ api }) => {
// Remove added contacts
await Promise.all([api.delete(`/livechat/visitor/${NEW_CONTACT.token}`), api.delete(`/livechat/visitor/${EXISTING_CONTACT.token}`)]);
});
test.beforeEach(async ({ page }) => {
poContacts = new OmnichannelContactCenterContacts(page);
poNavbar = new Navbar(page);
});
test.afterEach(async ({ api }) => {
await api
.get('/omnichannel/contacts.search', { searchText: NEW_CONTACT.phone })
.then((res) => res.json())
.then((res) => {
NEW_CONTACT.token = res.contacts?.[0]?.token;
NEW_CONTACT.id = res.contacts?.[0]?._id;
});
await api
.get('/omnichannel/contacts.search', { searchText: EXISTING_CONTACT.phones[0] })
.then((res) => res.json())
.then((res) => {
EXISTING_CONTACT.token = res.contacts?.[0]?.token;
EXISTING_CONTACT.id = res.contacts?.[0]?._id;
});
});
test.beforeEach(async ({ page }) => {
await new HomeOmnichannel(page).goto();
await poNavbar.btnContactCenter.click();
await poContacts.tabContacts.click();
await page.waitForURL(URL.contactCenter);
});
test('Add new contact', async ({ page }) => {
await test.step('cancel button', async () => {
await poContacts.btnNewContact.click();
await page.waitForURL(URL.newContact);
await expect(poContacts.editContact.inputName).toBeVisible();
await poContacts.editContact.btnCancel.click();
await page.waitForURL(URL.contactCenter);
await expect(poContacts.editContact.inputName).not.toBeVisible();
});
await test.step('open contextual bar', async () => {
await poContacts.btnNewContact.click();
await page.waitForURL(URL.newContact);
await expect(poContacts.editContact.inputName).toBeVisible();
});
await test.step('input name', async () => {
await poContacts.editContact.inputName.fill(NEW_CONTACT.name);
});
await test.step('validate email format', async () => {
await poContacts.editContact.btnAddEmail.click();
await poContacts.editContact.inputEmail.fill('invalidemail');
await page.keyboard.press('Enter');
await expect(poContacts.editContact.getErrorMessage(ERROR.invalidEmail)).toBeVisible();
});
await test.step('validate email is duplicated', async () => {
await poContacts.editContact.inputEmail.fill(EXISTING_CONTACT.emails[0]);
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.emailAlreadyExists)).toBeVisible();
});
await test.step('validate email is required', async () => {
await poContacts.editContact.inputEmail.clear();
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.emailRequired)).toBeVisible();
});
await test.step('input email', async () => {
await poContacts.editContact.inputEmail.fill(NEW_CONTACT.email);
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.invalidEmail)).not.toBeVisible();
await expect(poContacts.editContact.getErrorMessage(ERROR.emailRequired)).not.toBeVisible();
await expect(poContacts.editContact.getErrorMessage(ERROR.emailAlreadyExists)).not.toBeVisible();
});
await test.step('validate phone is duplicated', async () => {
await poContacts.editContact.btnAddPhone.click();
await poContacts.editContact.inputPhone.fill(EXISTING_CONTACT.phones[0]);
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.phoneAlreadyExists)).toBeVisible();
});
await test.step('input phone', async () => {
await poContacts.editContact.inputPhone.fill(NEW_CONTACT.phone);
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.phoneRequired)).not.toBeVisible();
});
await test.step('save new contact', async () => {
await expect(poContacts.editContact.btnSave).toBeEnabled();
await poContacts.editContact.btnSave.click();
await poContacts.inputSearch.fill(NEW_CONTACT.name);
await expect(poContacts.table.findRowByName(NEW_CONTACT.name)).toBeVisible();
});
});
test('Edit new contact', async ({ page }) => {
await test.step('search contact and open contextual bar', async () => {
await poContacts.inputSearch.fill(NEW_CONTACT.name);
const row = poContacts.table.findRowByName(NEW_CONTACT.name);
await expect(row).toBeVisible();
await row.click();
await page.waitForURL(URL.contactInfo);
});
await test.step('cancel button', async () => {
await poContacts.contactInfo.btnEdit.click();
await page.waitForURL(URL.editContact);
await poContacts.editContact.btnCancel.click();
await page.waitForURL(URL.contactInfo);
});
await test.step('edit contact', async () => {
await poContacts.contactInfo.btnEdit.click();
await page.waitForURL(URL.editContact);
});
await test.step('initial values', async () => {
await expect(poContacts.editContact.inputName).toHaveValue(NEW_CONTACT.name);
await expect(poContacts.editContact.inputEmail).toHaveValue(NEW_CONTACT.email);
await expect(poContacts.editContact.inputPhone).toHaveValue(NEW_CONTACT.phone);
});
await test.step('validate email format', async () => {
await poContacts.editContact.inputEmail.fill('invalidemail');
await page.keyboard.press('Enter');
await expect(poContacts.editContact.getErrorMessage(ERROR.invalidEmail)).toBeVisible();
});
await test.step('validate email is duplicated', async () => {
await poContacts.editContact.inputEmail.fill(EXISTING_CONTACT.emails[0]);
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.emailAlreadyExists)).toBeVisible();
});
await test.step('validate email is required', async () => {
await poContacts.editContact.inputEmail.clear();
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.emailRequired)).toBeVisible();
});
await test.step('validate name is required', async () => {
await poContacts.editContact.inputName.clear();
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.nameRequired)).toBeVisible();
});
await test.step('edit name', async () => {
await poContacts.editContact.inputName.fill(EDIT_CONTACT.name);
});
await test.step('validate phone is duplicated', async () => {
await poContacts.editContact.inputPhone.fill(EXISTING_CONTACT.phones[0]);
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.phoneAlreadyExists)).toBeVisible();
});
await test.step('input phone ', async () => {
await poContacts.editContact.inputPhone.fill(EDIT_CONTACT.phone);
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.phoneRequired)).not.toBeVisible();
});
await test.step('input email', async () => {
await poContacts.editContact.inputEmail.fill(EDIT_CONTACT.email);
await page.keyboard.press('Tab');
await expect(poContacts.editContact.getErrorMessage(ERROR.invalidEmail)).not.toBeVisible();
await expect(poContacts.editContact.getErrorMessage(ERROR.emailRequired)).not.toBeVisible();
await expect(poContacts.editContact.getErrorMessage(ERROR.emailAlreadyExists)).not.toBeVisible();
});
await test.step('save new contact ', async () => {
await poContacts.editContact.btnSave.click();
await poContacts.inputSearch.fill(EDIT_CONTACT.name);
await expect(poContacts.table.findRowByName(EDIT_CONTACT.name)).toBeVisible();
});
});
test('Delete a contact', async () => {
await test.step('Find contact and open modal', async () => {
await poContacts.inputSearch.fill(DELETE_CONTACT.name);
await poContacts.deleteContact(DELETE_CONTACT.name);
});
await test.step('Fill confirmation and delete contact', async () => {
// Fills the input with the wrong confirmation
await poContacts.deleteContactModal.inputConfirmation.fill('wrong');
await expect(poContacts.deleteContactModal.btnDelete).toBeDisabled();
// Fills the input correctly
await poContacts.deleteContactModal.inputConfirmation.fill('delete');
await expect(poContacts.deleteContactModal.btnDelete).toBeEnabled();
await poContacts.deleteContactModal.delete();
});
await test.step('Confirm contact removal', async () => {
await poContacts.inputSearch.fill(DELETE_CONTACT.name);
await expect(poContacts.table.findRowByName(DELETE_CONTACT.name)).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,345 @@
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { Navbar } from '../page-objects/fragments';
import { OmnichannelContactCenterChats } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments';
import { createConversation, updateRoom } from '../utils/omnichannel/rooms';
import { createTag } from '../utils/omnichannel/tags';
import { createOrUpdateUnit } from '../utils/omnichannel/units';
import { test, expect } from '../utils/test';
const URL = {
contactCenterChats: '/omnichannel-directory/chats',
};
const visitorA = createFakeVisitor().name;
const visitorB = createFakeVisitor().name;
const visitorC = createFakeVisitor().name;
test.skip(!IS_EE, 'OC - Contact center Filters > Enterprise Only');
test.use({ storageState: Users.admin.state });
test.describe('OC - Contact Center', async () => {
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let conversations: Awaited<ReturnType<typeof createConversation>>[];
let agents: Awaited<ReturnType<typeof createAgent>>[];
let tagA: Awaited<ReturnType<typeof createTag>>;
let tagB: Awaited<ReturnType<typeof createTag>>;
let units: Awaited<ReturnType<typeof createOrUpdateUnit>>[];
let poNavbar: Navbar;
let poOmniChats: OmnichannelContactCenterChats;
// Allow manual on hold
test.beforeAll(async ({ api }) => {
const responses = await Promise.all([
api.post('/settings/Livechat_allow_manual_on_hold', { value: true }),
api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: false }),
]);
responses.forEach((res) => expect(res.status()).toBe(200));
});
// Create departments
test.beforeAll(async ({ api }) => {
departments = await Promise.all([createDepartment(api), createDepartment(api)]);
});
// Create agents
test.beforeAll(async ({ api }) => {
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]);
const agentsStatuses = await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)));
agentsStatuses.forEach((res) => expect(res.status()).toBe(200));
});
// Add agents to departments
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
const promises = await Promise.all([
addAgentToDepartment(api, { department: departmentA, agentId: 'user1' }),
addAgentToDepartment(api, { department: departmentB, agentId: 'user2' }),
]);
promises.forEach((res) => expect(res.status()).toBe(200));
});
// Create tags
test.beforeAll(async ({ api }) => {
tagA = await createTag(api);
tagB = await createTag(api);
[tagA, tagB].forEach((res) => expect(res.response.status()).toBe(200));
});
// Create unit
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map((dep) => dep.data);
units = await Promise.all([
createOrUpdateUnit(api, {
monitors: [{ monitorId: 'user1', username: 'user1' }],
departments: [{ departmentId: departmentA._id }],
}),
createOrUpdateUnit(api, {
monitors: [{ monitorId: 'user2', username: 'user2' }],
departments: [{ departmentId: departmentB._id }],
}),
]);
units.forEach((res) => expect(res.response.status()).toBe(200));
});
// Create rooms
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
conversations = await Promise.all([
createConversation(api, {
visitorName: visitorA,
visitorToken: visitorA,
agentId: `user1`,
departmentId: departmentA._id,
}),
createConversation(api, {
visitorName: visitorB,
visitorToken: visitorB,
agentId: `user2`,
departmentId: departmentB._id,
}),
createConversation(api, {
visitorName: visitorC,
visitorToken: visitorC,
}),
]);
const [conversationA, conversationB] = conversations.map(({ data }) => data);
await Promise.all([
updateRoom(api, {
roomId: conversationA.room._id,
visitorId: conversationA.visitor._id,
tags: [tagA.data.name],
}),
updateRoom(api, {
roomId: conversationB.room._id,
visitorId: conversationB.visitor._id,
tags: [tagB.data.name],
}),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', true);
});
test.afterAll(async ({ api }) => {
await Promise.all([
// Delete conversations
...conversations.map((conversation) => conversation.delete()),
// Delete departments
...departments.map((department) => department.delete()),
// Delete agents
...agents.map((agent) => agent.delete()),
// Delete tags
...[tagA, tagB].map((tag) => tag.delete()),
// Delete units
...units.map((unit) => unit.delete()),
// Reset setting
api.post('/settings/Livechat_allow_manual_on_hold', { value: false }),
api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: true }),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', false);
});
// Change conversation A to on hold and close conversation B
test.beforeAll(async ({ api }) => {
const [conversationA, , conversationC] = conversations.map(({ data }) => data);
const statesPromises = await Promise.all([
api.post('/livechat/room.onHold', { roomId: conversationA.room._id }),
api.post('/livechat/room.close', {
rid: conversationC.room._id,
token: visitorC,
}),
]);
statesPromises.forEach((res) => expect(res.status()).toBe(200));
});
test.beforeEach(async ({ page }) => {
poOmniChats = new OmnichannelContactCenterChats(page);
poNavbar = new Navbar(page);
await new HomeOmnichannel(page).goto();
await poNavbar.btnContactCenter.click();
await poOmniChats.tabChats.click();
await page.waitForURL(URL.contactCenterChats);
});
test('OC - Contact Center - Filters', async () => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
const [unitA, unitB] = units.map((unit) => unit.data);
await test.step('expect to filter by guest', async () => {
await poOmniChats.inputSearch.fill(visitorA);
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).not.toBeVisible();
await expect(poOmniChats.btnSearchChip(visitorA)).toBeVisible();
await poOmniChats.inputSearch.fill('');
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
});
await test.step('expect to filter by Served By', async () => {
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await poOmniChats.btnFilters.click();
// Select user1
await poOmniChats.filters.selectServedBy('user1');
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.btnServedByChip('user1')).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).not.toBeVisible();
// Select user2
await poOmniChats.btnServedByChip('user1').locator('i').click();
await poOmniChats.filters.selectServedBy('user2');
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorA)).not.toBeVisible();
await expect(poOmniChats.btnServedByChip('user2')).toBeVisible();
// Select all users
await poOmniChats.btnServedByChip('user2').locator('i').click();
await poOmniChats.filters.selectServedBy('user1');
await poOmniChats.filters.selectServedBy('user2');
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await poOmniChats.btnServedByChip('user1').locator('i').click();
});
await test.step('expect to filter by status', async () => {
await poOmniChats.filters.selectStatus('Closed');
await expect(poOmniChats.table.findRowByName(visitorA)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).toBeVisible();
await expect(poOmniChats.btnStatusChip('Closed')).toBeVisible();
await poOmniChats.filters.selectStatus('Open');
await expect(poOmniChats.table.findRowByName(visitorA)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).not.toBeVisible();
await expect(poOmniChats.btnStatusChip('Open')).toBeVisible();
await poOmniChats.filters.selectStatus('all');
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).toBeVisible();
await poOmniChats.filters.selectStatus('On hold');
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).not.toBeVisible();
await expect(poOmniChats.btnStatusChip('On hold')).toBeVisible();
await poOmniChats.btnStatusChip('On hold').locator('i').click();
});
await test.step('expect to filter by department', async () => {
// select department A
await poOmniChats.filters.selectDepartment(departmentA.name);
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).not.toBeVisible();
await expect(poOmniChats.btnDepartmentChip(departmentA.name)).toBeVisible();
await poOmniChats.btnDepartmentChip(departmentA.name).locator('i').click();
// select department B
await poOmniChats.filters.selectDepartment(departmentB.name);
await expect(poOmniChats.table.findRowByName(visitorA)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).not.toBeVisible();
await expect(poOmniChats.btnDepartmentChip(departmentB.name)).toBeVisible();
await poOmniChats.btnDepartmentChip(departmentB.name).locator('i').click();
// select all departments
await poOmniChats.filters.selectDepartment(departmentA.name);
await poOmniChats.filters.selectDepartment(departmentB.name);
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
});
await test.step('expect to filter by tags', async () => {
await poOmniChats.filters.selectTag(tagA.data.name);
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).not.toBeVisible();
await poOmniChats.filters.selectTag(tagB.data.name);
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await poOmniChats.filters.removeTag(tagA.data.name);
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorA)).not.toBeVisible();
await poOmniChats.filters.removeTag(tagB.data.name);
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
});
await test.step('expect to filter by units', async () => {
// select unitA
await poOmniChats.filters.selectUnit(unitA.name);
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).not.toBeVisible();
await poOmniChats.btnUnitsChip(unitA.name).locator('i').click();
// select unitB
await poOmniChats.filters.selectUnit(unitB.name);
await expect(poOmniChats.table.findRowByName(visitorA)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).not.toBeVisible();
await poOmniChats.btnUnitsChip(unitB.name).locator('i').click();
// no unit selected
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).not.toBeVisible();
});
});
test('OC - Contact Center - Clear all applied Filters', async () => {
const [departmentA] = departments.map(({ data }) => data);
const [unitA] = units.map((unit) => unit.data);
await test.step('expect to display result as per applied filters ', async () => {
await poOmniChats.btnFilters.click();
await poOmniChats.filters.selectServedBy('user1');
await poOmniChats.filters.selectStatus('On hold');
await poOmniChats.filters.selectDepartment(departmentA.name);
await poOmniChats.filters.selectTag(tagA.data.name);
await poOmniChats.filters.selectUnit(unitA.name);
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).not.toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).not.toBeVisible();
});
await test.step('expect to clear all filters ', async () => {
await poOmniChats.filters.btnClearFilters.click();
await expect(poOmniChats.table.findRowByName(visitorA)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorB)).toBeVisible();
await expect(poOmniChats.table.findRowByName(visitorC)).toBeVisible();
});
});
test('OC - Contact Center - Close contextual bar with filter screen', async () => {
await test.step('expect to close filters contextual bar ', async () => {
await poOmniChats.btnFilters.click();
await poOmniChats.filters.close();
await expect(poOmniChats.btnFilters).toBeVisible();
await expect(poOmniChats.filters.btnClearFilters).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,84 @@
import { faker } from '@faker-js/faker';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { createCustomField } from '../utils/omnichannel/custom-field';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
const visitor = createFakeVisitor();
test.skip(!IS_EE, 'Omnichannel Contact Review > Enterprise Only');
test.use({ storageState: Users.user1.state });
test.describe.serial('OC - Contact Review', () => {
let poHomeChannel: HomeOmnichannel;
const customFieldName = faker.string.uuid();
const visitorToken = faker.string.uuid();
let conversation: Awaited<ReturnType<typeof createConversation>>;
let customField: Awaited<ReturnType<typeof createCustomField>>;
test.beforeAll(async ({ api }) => {
(
await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
])
).every((res) => expect(res.status()).toBe(200));
customField = await createCustomField(api, { field: customFieldName });
});
test.beforeEach(async ({ page }) => {
poHomeChannel = new HomeOmnichannel(page);
});
test.beforeEach(async () => {
await poHomeChannel.goto();
});
test.beforeEach(async ({ api }) => {
conversation = await createConversation(api, { visitorName: visitor.name, agentId: `user1`, visitorToken });
});
test.beforeEach(async ({ api }) => {
const resCustomFieldA = await api.post('/livechat/custom.field', {
token: visitorToken,
key: customFieldName,
value: 'custom-field-value',
overwrite: true,
});
expect(resCustomFieldA.status()).toBe(200);
const resCustomFieldB = await api.post('/livechat/custom.field', {
token: visitorToken,
key: customFieldName,
value: 'custom-field-value-2',
overwrite: false,
});
expect(resCustomFieldB.status()).toBe(200);
});
test.afterAll(async ({ api }) => {
(await Promise.all([api.delete('/livechat/users/agent/user1'), api.delete('/livechat/users/manager/user1')])).every((res) =>
expect(res.status()).toBe(200),
);
await conversation.delete();
await customField.delete();
});
test('OC - Contact Review - Update custom field conflicting', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitor.name).click();
await poHomeChannel.roomToolbar.openContactInfo();
await poHomeChannel.contacts.contactInfo.solveConflict(customFieldName, 'custom-field-value-2');
await expect(poHomeChannel.contacts.contactInfo.btnSeeConflicts).not.toBeVisible();
});
});

View File

@@ -0,0 +1,61 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { expect, test } from '../utils/test';
test.describe('Omnichannel contact info', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeOmnichannel };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
// Set user user 1 as manager and agent
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeChannel: new HomeOmnichannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterAll(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await api.delete('/livechat/users/manager/user1');
await agent.page.close();
});
test('Receiving a message from visitor, and seeing its information', async () => {
await test.step('Expect send a message as a visitor', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('Expect to be able to see contact information and edit', async () => {
await agent.poHomeChannel.roomToolbar.openContactInfo();
await agent.poHomeChannel.contacts.contactInfo.btnEdit.click();
});
await test.step('Expect to update room name and subscription when updating contact name', async () => {
await agent.poHomeChannel.contacts.editContact.inputName.fill('Edited Contact Name');
await agent.poHomeChannel.contacts.editContact.btnSave.click();
await expect(agent.poHomeChannel.sidebar.channelsList.getByText('Edited Contact Name')).toBeVisible();
await expect(agent.poHomeChannel.content.channelHeader.getByText('Edited Contact Name')).toBeVisible();
});
});
});

View File

@@ -0,0 +1,70 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeChannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { expect, test } from '../utils/test';
test.describe('OC - Contact Unknown Callout', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeChannel };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeChannel: new HomeChannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.beforeEach('create livechat conversation', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
test.afterEach('close livechat conversation', async () => {
await poLiveChat.closeChat();
});
test.afterAll(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await api.delete('/livechat/users/manager/user1');
await agent.page.close();
});
test('OC - Contact Unknown Callout - Dismiss callout', async () => {
await test.step('expect to open conversation', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('expect contact unknown callout to be visible', async () => {
await expect(agent.poHomeChannel.content.contactUnknownCallout).toBeVisible();
});
await test.step('expect to hide callout when dismiss is clicked', async () => {
await agent.poHomeChannel.content.btnDismissContactUnknownCallout.click();
await expect(agent.poHomeChannel.content.contactUnknownCallout).not.toBeVisible();
});
await test.step('expect keep callout hidden after changing pages', async () => {
await agent.poHomeChannel.navbar.btnHome.click();
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
await expect(agent.poHomeChannel.content.contactUnknownCallout).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,154 @@
import { faker } from '@faker-js/faker';
import { createFakeVisitor } from '../../mocks/data';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { createAgent } from '../utils/omnichannel/agents';
import { createCustomField, setVisitorCustomFieldValue } from '../utils/omnichannel/custom-field';
import { createManager } from '../utils/omnichannel/managers';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
const visitor = createFakeVisitor();
test.use({ storageState: Users.user1.state });
test.describe.serial('OC - Custom fields usage, scope : room and visitor', () => {
let poHomeChannel: HomeOmnichannel;
const roomCustomFieldLabel = `room_cf_${faker.string.alpha(8)}`;
const roomCustomFieldName = roomCustomFieldLabel;
const roomCustomFieldValue = faker.lorem.words(3);
const visitorCustomFieldLabel = `visitor_cf_${faker.string.alpha(8)}`;
const visitorCustomFieldName = visitorCustomFieldLabel;
const visitorCustomFieldValue = faker.lorem.words(3);
const visitorToken = faker.string.uuid();
let agent: Awaited<ReturnType<typeof createAgent>>;
let manager: Awaited<ReturnType<typeof createManager>>;
let conversation: Awaited<ReturnType<typeof createConversation>>;
let roomCustomField: Awaited<ReturnType<typeof createCustomField>>;
let visitorCustomField: Awaited<ReturnType<typeof createCustomField>>;
test.beforeAll('Set up agent, manager and custom fields', async ({ api }) => {
[agent, manager] = await Promise.all([createAgent(api, 'user1'), createManager(api, 'user1')]);
[roomCustomField, visitorCustomField] = await Promise.all([
createCustomField(api, {
field: roomCustomFieldLabel,
label: roomCustomFieldName,
scope: 'room',
}),
createCustomField(api, {
field: visitorCustomFieldLabel,
label: visitorCustomFieldName,
scope: 'visitor',
}),
]);
});
test.beforeEach(async ({ page, api }) => {
poHomeChannel = new HomeOmnichannel(page);
await poHomeChannel.goto();
conversation = await createConversation(api, {
visitorName: visitor.name,
agentId: 'user1',
visitorToken,
});
await setVisitorCustomFieldValue(api, {
token: visitorToken,
customFieldId: visitorCustomField.customField._id,
value: visitorCustomFieldValue,
});
});
test.afterAll('Remove agent, manager, custom fields and conversation', async () => {
await Promise.all([agent.delete(), manager.delete(), roomCustomField.delete(), visitorCustomField.delete(), conversation.delete()]);
});
test('Should be allowed to set room custom field for a conversation', async () => {
await test.step('Agent opens the conversation', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitor.name).click();
});
await test.step('Agent opens edit room', async () => {
await poHomeChannel.roomInfo.waitForDisplay();
await poHomeChannel.roomInfo.btnEdit.click();
await poHomeChannel.editRoomInfo.waitForDisplay();
});
await test.step('Agent fills room custom field and saves', async () => {
await poHomeChannel.editRoomInfo.getRoomCustomField(roomCustomFieldLabel).fill(roomCustomFieldValue);
await poHomeChannel.editRoomInfo.btnSave.click();
});
await test.step('Custom field should be updated successfully', async () => {
await poHomeChannel.roomInfo.btnEdit.click();
await poHomeChannel.editRoomInfo.waitForDisplay();
await expect(poHomeChannel.editRoomInfo.getRoomCustomField(roomCustomFieldLabel)).toHaveValue(roomCustomFieldValue);
});
});
test('Should be allowed to update existing room custom field', async () => {
const updatedValue = faker.lorem.words(2);
await test.step('Agent opens the conversation', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitor.name).click();
});
await test.step('Agent opens edit room and updates custom field', async () => {
await poHomeChannel.roomInfo.waitForDisplay();
await poHomeChannel.roomInfo.btnEdit.click();
await poHomeChannel.editRoomInfo.waitForDisplay();
await poHomeChannel.editRoomInfo.getRoomCustomField(roomCustomFieldLabel).fill(updatedValue);
await poHomeChannel.editRoomInfo.btnSave.click();
});
await test.step('Room Information displays the updated custom field value', async () => {
await poHomeChannel.roomInfo.btnEdit.click();
await poHomeChannel.editRoomInfo.waitForDisplay();
await expect(poHomeChannel.editRoomInfo.getRoomCustomField(roomCustomFieldLabel)).toHaveValue(updatedValue);
});
});
test('Should verify that the visitor custom field is set', async () => {
await test.step('Agent opens the conversation', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitor.name).click();
});
await test.step('Agent opens Contact Information', async () => {
await poHomeChannel.roomToolbar.openContactInfo();
await poHomeChannel.contacts.contactInfo.waitForDisplay();
});
await test.step('Assert custom field is set successfully', async () => {
await expect(poHomeChannel.contacts.contactInfo.getInfoByValue(visitorCustomFieldValue)).toBeVisible();
});
});
test('Should be allowed to update existing visitor custom field', async () => {
const updatedVisitorCustomFieldValue = faker.lorem.words(2);
await test.step('Agent opens the conversation', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitor.name).click();
});
await test.step('Agent opens Contact Information', async () => {
await poHomeChannel.roomToolbar.openContactInfo();
await poHomeChannel.contacts.contactInfo.waitForDisplay();
});
await test.step('Agent clicks edit and updates visitor custom field', async () => {
await poHomeChannel.contacts.contactInfo.btnEdit.click();
await poHomeChannel.contacts.contactInfo.getVisitorCustomField(visitorCustomFieldLabel).fill(updatedVisitorCustomFieldValue);
await poHomeChannel.contacts.contactInfo.btnSave.click();
});
await test.step('Assert custom field is updated successfully', async () => {
await expect(poHomeChannel.contacts.contactInfo.getInfoByValue(updatedVisitorCustomFieldValue)).toBeVisible();
});
});
});

View File

@@ -0,0 +1,47 @@
import { Users } from '../fixtures/userStates';
import { OmnichannelCustomFields } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe('omnichannel-customFields', () => {
let poOmnichannelCustomFields: OmnichannelCustomFields;
const newField = 'any_field';
test.beforeEach(async ({ page }) => {
poOmnichannelCustomFields = new OmnichannelCustomFields(page);
await poOmnichannelCustomFields.goto();
});
test('expect add new "custom field"', async ({ page }) => {
await poOmnichannelCustomFields.createNew();
await page.waitForURL('/omnichannel/customfields/new');
await poOmnichannelCustomFields.manageCustomFields.inputField.fill(newField);
await poOmnichannelCustomFields.manageCustomFields.inputLabel.fill('any_label');
await poOmnichannelCustomFields.manageCustomFields.save();
await expect(poOmnichannelCustomFields.table.findRowByName(newField)).toBeVisible();
});
test('expect update "newField"', async () => {
const newLabel = 'new_any_label';
await poOmnichannelCustomFields.inputSearch.fill(newField);
await poOmnichannelCustomFields.table.findRowByName(newField).click();
await poOmnichannelCustomFields.manageCustomFields.inputLabel.fill('new_any_label');
await poOmnichannelCustomFields.manageCustomFields.labelVisible.click();
await poOmnichannelCustomFields.manageCustomFields.save();
await expect(poOmnichannelCustomFields.table.findRowByName(newField)).toContainText(newLabel);
});
test('expect remove "new_field"', async () => {
await poOmnichannelCustomFields.inputSearch.fill(newField);
await poOmnichannelCustomFields.table.findRowByName(newField).click();
await poOmnichannelCustomFields.deleteCustomField(newField);
await poOmnichannelCustomFields.inputSearch.fill(newField);
await expect(poOmnichannelCustomFields.table.findRowByName(newField)).toBeHidden();
});
});

View File

@@ -0,0 +1,39 @@
import { faker } from '@faker-js/faker';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelDepartments } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe.serial('OC - Manage Departments (CE)', () => {
test.skip(IS_EE, 'Community Edition Only');
let poOmnichannelDepartments: OmnichannelDepartments;
let departmentName: string;
test.beforeAll(async () => {
departmentName = faker.string.uuid();
});
test.beforeEach(async ({ page }) => {
poOmnichannelDepartments = new OmnichannelDepartments(page);
await poOmnichannelDepartments.goto();
});
test('OC - Manage Departments (CE) - Create department', async () => {
await test.step('expect create new department', async () => {
await poOmnichannelDepartments.createNew();
await poOmnichannelDepartments.createDepartment(departmentName, faker.internet.email());
await poOmnichannelDepartments.inputSearch.fill(departmentName);
await expect(poOmnichannelDepartments.table.findRowByName(departmentName)).toBeVisible();
});
await test.step('expect to not be possible adding a second department ', async () => {
await poOmnichannelDepartments.createNew();
await poOmnichannelDepartments.upsellDepartmentsModal.waitForDisplay();
await poOmnichannelDepartments.upsellDepartmentsModal.close();
});
});
});

View File

@@ -0,0 +1,256 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelDepartments } from '../page-objects/omnichannel';
import { createAgent } from '../utils/omnichannel/agents';
import { createDepartment, deleteDepartment } from '../utils/omnichannel/departments';
import { test, expect } from '../utils/test';
const ERROR = {
requiredName: 'Name required',
requiredEmail: 'Email required',
invalidEmail: 'Invalid email address',
};
test.use({ storageState: Users.admin.state });
test.describe('OC - Manage Departments', () => {
test.skip(!IS_EE, 'Enterprise Edition Only');
let poOmnichannelDepartments: OmnichannelDepartments;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ api }) => {
// turn on department removal
await api.post('/settings/Omnichannel_enable_department_removal', { value: true });
agent = await createAgent(api, 'user1');
});
test.afterAll(async ({ api }) => {
// turn off department removal
await api.post('/settings/Omnichannel_enable_department_removal', { value: false });
await agent.delete();
});
test.describe('Create first department', async () => {
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannelDepartments = new OmnichannelDepartments(page);
await poOmnichannelDepartments.goto();
});
test('Create department', async ({ page }) => {
const departmentName = faker.string.uuid();
await poOmnichannelDepartments.createNew();
await test.step('expect name and email to be required', async () => {
await expect(poOmnichannelDepartments.errorMessage(ERROR.requiredName)).not.toBeVisible();
await poOmnichannelDepartments.btnSave.click();
await expect(poOmnichannelDepartments.errorMessage(ERROR.requiredName)).toBeVisible();
await expect(poOmnichannelDepartments.errorMessage(ERROR.requiredEmail)).toBeVisible();
});
await test.step('expect to fill required fields', async () => {
await poOmnichannelDepartments.labelEnabled.click();
await poOmnichannelDepartments.inputName.fill(departmentName);
await poOmnichannelDepartments.inputEmail.fill(faker.internet.email());
});
await test.step('expect to fetch agents a reasonable number of times', async () => {
let requestCount = 0;
await page.route('**/v1/livechat/users/agent*', async (route) => {
requestCount++;
await route.continue();
});
await poOmnichannelDepartments.inputAgents.click();
await poOmnichannelDepartments.inputAgents.fill('user1');
await page.waitForTimeout(1000);
await expect(requestCount).toBeGreaterThan(0);
await expect(requestCount).toBeLessThan(3);
await poOmnichannelDepartments.inputAgents.click();
});
await test.step('expect to add an agent', async () => {
await poOmnichannelDepartments.inputAgents.click();
await poOmnichannelDepartments.inputAgents.fill('user1');
await poOmnichannelDepartments.findOption('user1 (@user1)').click();
await poOmnichannelDepartments.btnAddAgent.click();
await expect(poOmnichannelDepartments.agentsTable.findRowByName('user1')).toBeVisible();
});
await test.step('expect create new department', async () => {
await poOmnichannelDepartments.btnSave.click();
await poOmnichannelDepartments.search(departmentName);
await expect(poOmnichannelDepartments.table.findRowByName(departmentName)).toBeVisible();
});
await test.step('expect to delete department', async () => {
await poOmnichannelDepartments.search(departmentName);
await poOmnichannelDepartments.getDepartmentMenuByName(departmentName).click();
await poOmnichannelDepartments.menuDeleteOption.click();
await test.step('expect confirm delete department', async () => {
await test.step('expect delete to be disabled when name is incorrect', async () => {
await expect(poOmnichannelDepartments.deleteModal.btnDelete).toBeDisabled();
await poOmnichannelDepartments.deleteModal.inputConfirmDepartmentName.fill('someramdomname');
await expect(poOmnichannelDepartments.deleteModal.btnDelete).toBeDisabled();
});
await test.step('expect to successfuly delete if department name is correct', async () => {
await expect(poOmnichannelDepartments.deleteModal.btnDelete).toBeDisabled();
await poOmnichannelDepartments.deleteModal.inputConfirmDepartmentName.fill(departmentName);
await expect(poOmnichannelDepartments.deleteModal.btnDelete).toBeEnabled();
await poOmnichannelDepartments.deleteModal.deleteDepartment(departmentName);
});
});
await test.step('expect department to have been deleted', async () => {
await poOmnichannelDepartments.search(departmentName);
await expect(poOmnichannelDepartments.table.findRowByName(departmentName)).toHaveCount(0);
});
});
});
});
test.describe('After creation', async () => {
let department: Awaited<ReturnType<typeof createDepartment>>['data'];
test.beforeEach(async ({ api, page }) => {
poOmnichannelDepartments = new OmnichannelDepartments(page);
department = await createDepartment(api).then((res) => res.data);
await poOmnichannelDepartments.goto();
});
test.afterEach(async ({ api }) => {
await deleteDepartment(api, { id: department._id });
});
test('Edit department', async () => {
await test.step('expect create new department', async () => {
await poOmnichannelDepartments.search(department.name);
await expect(poOmnichannelDepartments.table.findRowByName(department.name)).toBeVisible();
return department;
});
await test.step('expect update department name', async () => {
await poOmnichannelDepartments.search(department.name);
await poOmnichannelDepartments.getDepartmentMenuByName(department.name).click();
await poOmnichannelDepartments.menuEditOption.click();
await poOmnichannelDepartments.inputName.fill(`edited-${department.name}`);
await poOmnichannelDepartments.btnSave.click();
await poOmnichannelDepartments.search(`edited-${department.name}`);
await expect(poOmnichannelDepartments.table.findRowByName(`edited-${department.name}`)).toBeVisible();
});
});
test('Archive department', async () => {
await test.step('expect create new department', async () => {
await poOmnichannelDepartments.search(department.name);
await expect(poOmnichannelDepartments.table.findRowByName(department.name)).toBeVisible();
});
await test.step('expect archive department', async () => {
await poOmnichannelDepartments.search(department.name);
await expect(poOmnichannelDepartments.table.findRowByName(department.name)).toBeVisible();
await poOmnichannelDepartments.archiveDepartmentByName(department.name);
await poOmnichannelDepartments.tabArchivedDepartments.click();
await poOmnichannelDepartments.search(department.name);
await expect(poOmnichannelDepartments.table.findRowByName(department.name)).toBeVisible();
});
await test.step('expect archived department to not be editable', async () => {
await poOmnichannelDepartments.getDepartmentMenuByName(department.name).click();
await expect(poOmnichannelDepartments.menuEditOption).not.toBeVisible();
});
await test.step('expect unarchive department', async () => {
await poOmnichannelDepartments.menuUnarchiveOption.click();
await expect(poOmnichannelDepartments.table.findRowByName(department.name)).toHaveCount(0);
});
});
test('Request tag(s) before closing conversation', async () => {
await test.step('should create new department', async () => {
await poOmnichannelDepartments.search(department.name);
await expect(poOmnichannelDepartments.table.findRowByName(department.name)).toBeVisible();
});
const tagName = faker.string.sample(5);
await poOmnichannelDepartments.getDepartmentMenuByName(department.name).click();
await poOmnichannelDepartments.menuEditOption.click();
await test.step('should be able to add a tag properly', async () => {
await poOmnichannelDepartments.inputConversationClosingTags.fill(tagName);
await poOmnichannelDepartments.btnAddTags.click();
await expect(poOmnichannelDepartments.btnTag(tagName)).toBeVisible();
await expect(poOmnichannelDepartments.btnSave).toBeEnabled();
});
await test.step('should be able to remove a tag properly', async () => {
await poOmnichannelDepartments.btnTag(tagName).click();
await expect(poOmnichannelDepartments.btnAddTags).toBeDisabled();
});
await test.step('should not be possible to add empty tags', async () => {
await poOmnichannelDepartments.inputConversationClosingTags.fill('');
await expect(poOmnichannelDepartments.btnAddTags).toBeDisabled();
});
await test.step('should not be possible to add same tag twice', async () => {
const tagName = faker.string.sample(5);
await poOmnichannelDepartments.inputConversationClosingTags.fill(tagName);
await poOmnichannelDepartments.btnAddTags.click();
await poOmnichannelDepartments.inputConversationClosingTags.fill(tagName);
await expect(poOmnichannelDepartments.btnAddTags).toBeDisabled();
});
});
test('Toggle department removal', async ({ api, page }) => {
await test.step('expect create new department', async () => {
await poOmnichannelDepartments.search(department.name);
await expect(poOmnichannelDepartments.table.findRowByName(department.name)).toBeVisible();
});
await test.step('expect to be able to delete department', async () => {
await poOmnichannelDepartments.search(department.name);
await poOmnichannelDepartments.getDepartmentMenuByName(department.name).click();
await expect(poOmnichannelDepartments.menuDeleteOption).toBeEnabled();
await page.keyboard.press('Escape');
});
await test.step('expect to disable department removal setting', async () => {
const statusCode = (await api.post('/settings/Omnichannel_enable_department_removal', { value: false })).status();
expect(statusCode).toBe(200);
});
await test.step('expect not to be able to delete department', async () => {
await poOmnichannelDepartments.search(department.name);
await poOmnichannelDepartments.getDepartmentMenuByName(department.name).click();
await expect(poOmnichannelDepartments.menuDeleteOption).toBeDisabled();
await page.keyboard.press('Escape');
});
await test.step('expect to enable department removal setting', async () => {
const statusCode = (await api.post('/settings/Omnichannel_enable_department_removal', { value: true })).status();
expect(statusCode).toBe(200);
});
await test.step('expect to delete department', async () => {
const deleteRes = await deleteDepartment(api, { id: department._id });
expect(deleteRes.status()).toBe(200);
});
});
});
});

View File

@@ -0,0 +1,32 @@
import type { Page } from '@playwright/test';
import { ADMIN_CREDENTIALS, IS_EE } from '../config/constants';
import injectInitialData from '../fixtures/inject-initial-data';
import { Login } from '../page-objects';
import { test, expect } from '../utils/test';
// TODO: Replace this test with a unit test
test.describe('OC - Enterprise Menu Items After Relogin', () => {
// Create page object and redirect to home
test.beforeEach(async ({ page }: { page: Page }) => {
await new Login(page).goto('/omnichannel/current');
await page.locator('role=textbox[name=/username/i]').fill(ADMIN_CREDENTIALS.email);
await page.locator('[name=password]').fill(ADMIN_CREDENTIALS.password);
await page.locator('role=button[name="Login"]').click();
await page.locator('#main-content').waitFor();
});
// Delete all data
test.afterAll(async () => {
await injectInitialData();
});
test('OC - Enterprise Menu Items - Logout & Login', async ({ page }) => {
test.skip(!IS_EE);
await test.step('expect EE menu items to be visible', async () => {
await expect(page.locator('a[href="/omnichannel/tags"]')).toBeVisible();
});
});
});

View File

@@ -0,0 +1,129 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.user1.state });
test.describe('OC - Routing to Idle Agents', () => {
test.skip(!IS_EE, 'Enterprise Edition Only');
let poHomeOmnichannel: HomeOmnichannel;
let poLivechat: OmnichannelLiveChat;
let livechatPage: Page | undefined;
let agent: Awaited<ReturnType<typeof createAgent>>;
let visitor: { name: string; email: string };
const routingMethods = ['Auto_Selection', 'Load_Balancing', 'Load_Rotation'];
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
await expect(agent.response).toBeOK();
const settingResponses = await Promise.all([
setSettingValueById(api, 'Accounts_Default_User_Preferences_idleTimeLimit', 300),
setSettingValueById(api, 'Omnichannel_enable_department_removal', true),
]);
for (const response of settingResponses) {
await expect(response).toBeOK();
}
});
test.beforeEach(async ({ page, browser, api }) => {
visitor = createFakeVisitor();
await expect(await makeAgentAvailable(api, agent.data._id)).toBeOK();
poHomeOmnichannel = new HomeOmnichannel(page);
await poHomeOmnichannel.goto();
await expect(poHomeOmnichannel.navbar.getUserStatusBadge('online')).toBeVisible();
({ page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false));
poLivechat = new OmnichannelLiveChat(livechatPage, api);
});
test.afterEach(async ({ api }) => {
if (livechatPage) {
await livechatPage.context().close();
}
await expect(await setSettingValueById(api, 'Accounts_Default_User_Preferences_idleTimeLimit', 300)).toBeOK();
});
test.afterAll(async ({ api }) => {
const responses = await Promise.all([
agent.delete(),
setSettingValueById(api, 'Livechat_Routing_Method', 'Auto_Selection'),
setSettingValueById(api, 'Livechat_enabled_when_agent_idle', false),
setSettingValueById(api, 'Omnichannel_enable_department_removal', false),
]);
for (const response of responses) {
await expect(response).toBeOK();
}
});
routingMethods.forEach((routingMethod) => {
test.describe(`Routing method: ${routingMethod}`, () => {
test(`should not route to idle agents`, async ({ api }) => {
await test.step(`Setup routing method to ${routingMethod} and ignore idle agents`, async () => {
await expect(await setSettingValueById(api, 'Livechat_Routing_Method', routingMethod)).toBeOK();
await expect(await setSettingValueById(api, 'Livechat_enabled_when_agent_idle', false)).toBeOK();
});
await test.step('Visitor tries to initiate a conversation with the away agent', async () => {
await poLivechat.openAnyLiveChat();
await poLivechat.sendMessage(visitor, false);
await poLivechat.onlineAgentMessage.fill('Hello from visitor');
// Force Agent to become away by idle timeout
await expect(await setSettingValueById(api, 'Accounts_Default_User_Preferences_idleTimeLimit', 1)).toBeOK();
await poHomeOmnichannel.page.reload();
await expect(poHomeOmnichannel.navbar.getUserStatusBadge('away')).toBeVisible();
await poLivechat.btnSendMessageToOnlineAgent.click();
});
await test.step('Verify visitor is not taken by agent', async () => {
await expect(
poLivechat.alertMessage('Error starting a new conversation: Sorry, no online agents [no-agent-online]'),
).toBeVisible();
});
});
test(`should route to agents even if they are idle when setting is enabled`, async ({ api }) => {
await test.step(`Setup routing method to ${routingMethod} and allow idle agents`, async () => {
await expect(await setSettingValueById(api, 'Livechat_Routing_Method', routingMethod)).toBeOK();
await expect(await setSettingValueById(api, 'Livechat_enabled_when_agent_idle', true)).toBeOK();
});
await test.step('Force agent to become away by idle timeout', async () => {
await expect(await setSettingValueById(api, 'Accounts_Default_User_Preferences_idleTimeLimit', 1)).toBeOK();
await poHomeOmnichannel.page.reload();
await expect(poHomeOmnichannel.navbar.getUserStatusBadge('away')).toBeVisible();
});
await test.step('Visitor initiates chat', async () => {
await poLivechat.openAnyLiveChatAndSendMessage({
liveChatUser: visitor,
message: 'test message',
isOffline: false,
});
});
await test.step('Verify chat is served to an agent', async () => {
await expect(poLivechat.headerTitle).toHaveText(agent.data.username);
});
});
});
});
});

View File

@@ -0,0 +1,927 @@
import { faker } from '@faker-js/faker/locale/af_ZA';
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChatEmbedded } from '../page-objects/omnichannel';
import { createAgent } from '../utils/omnichannel/agents';
import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments';
import { test, expect } from '../utils/test';
// TODO: Use official widget typing once that is merged
declare const window: Window & {
// TODO: Improve tests to no longer use window object properties
onPrechatFormSubmit: boolean;
onAssignAgent: boolean;
onAgentStatusChange: boolean;
onOfflineFormSubmit: boolean;
onChatStarted: boolean;
onChatEnded: boolean;
RocketChat: {
livechat: {
clearBusinessUnit: () => void;
clearDepartment: () => void;
initialize: () => void;
maximizeWidget: () => void;
minimizeWidget: () => void;
hideWidget: () => void;
showWidget: () => void;
pageVisited: () => void;
registerGuest: (visitor: { name: string; email: string; token: string }) => void;
setAgent: (agent: { username: string; _id: string }) => void;
setBusinessUnit: (businessUnit?: string) => void;
setCustomField: (field: { key: string; value: string }) => void;
setDepartment: (dep: string) => void;
setGuestEmail: (email: string) => void;
setGuestName: (name: string) => void;
setGuestToken: (token: string) => void;
setParentUrl: (url: string) => void;
setTheme: (theme: {
color?: string;
fontColor?: string;
iconColor?: string;
title?: string;
offlineTitle?: string;
hideExpandChat?: boolean;
}) => void;
setLanguage: (language: string) => void;
transferChat: (department: string) => void;
onChatMaximized: (callback: () => void) => void;
onChatMinimized: (callback: () => void) => void;
onChatStarted: (callback: () => void) => void;
onChatEnded: (callback: () => void) => void;
onPrechatFormSubmit: (callback: () => void) => void;
onAssignAgent: (callback: () => void) => void;
onAgentStatusChange: (callback: () => void) => void;
onOfflineFormSubmit: (callback: () => void) => void;
onWidgetHidden: (callback: () => void) => void;
onWidgetShown: (callback: () => void) => void;
onServiceOffline: (callback: () => void) => void;
onQueuePositionChange: (callback: () => void) => void;
};
};
};
const createFakeVisitorRegistration = (extra?: { department?: string }) => ({
...createFakeVisitor(),
token: faker.string.uuid(),
...extra,
});
test.describe('OC - Livechat API', () => {
// TODO: Check if there is a way to add livechat to the global window object
test.describe('Basic Widget Interactions', () => {
// Tests that rely only on the widget itself, without requiring further interaction from the main RC app
let poAuxContext: { page: Page; poHomeOmnichannel: HomeOmnichannel };
let poLiveChat: OmnichannelLiveChatEmbedded;
let page: Page;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ browser, api }) => {
agent = await createAgent(api, 'user1');
page = await browser.newPage();
poLiveChat = new OmnichannelLiveChatEmbedded(page);
const { page: pageCtx } = await createAuxContext(browser, Users.user1);
poAuxContext = { page: pageCtx, poHomeOmnichannel: new HomeOmnichannel(pageCtx) };
await poLiveChat.goto();
});
test.afterAll(async () => {
await Promise.all([poAuxContext.page.close(), agent.delete(), page.close()]);
});
test('OC - Livechat API - Open and Close widget', async () => {
await test.step('Expect widget to be visible after maximizeWidget()', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
});
await test.step('Expect widget not be visible after minimizeWidget()', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.minimizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
});
});
test('OC - Livechat API - Show and Hide widget', async () => {
await test.step('Expect livechat button not be visible after minimizeWidget()', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.hideWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByRole('button', { name: 'Rocket.Chat' })).not.toBeVisible();
});
await test.step('Expect livechat button to be visible after show()', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.showWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByRole('button', { name: 'Rocket.Chat' })).toBeVisible();
});
});
test.skip('OC - Livechat API - setAgent', async () => {
// Set agent does not actually set the agent, it just sets the default agent on the widget state
// Maybe that is used in an integration? Since as it is now, when the user starts a chat, the agent will be overriden
// TODO: Find the use case of the setAgent method
await test.step('Expect setAgent to do something', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setAgent({ username: 'user1', _id: 'user1' }));
});
});
test('OC - Livechat API - setLanguage', async () => {
await test.step('Expect language to be pt-BR', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setLanguage('pt-BR'));
await expect(
page.frameLocator('#rocketchat-iframe').getByText('Por favor, conte-nos algumas informações para iniciarmos o chat'),
).toBeVisible();
});
await test.step('Expect language to be en', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setLanguage('en'));
await expect(page.frameLocator('#rocketchat-iframe').getByText('Please, tell us some information to start the chat')).toBeVisible();
});
});
test('OC - Livechat API - setTheme', async () => {
const registerGuestVisitor = createFakeVisitorRegistration();
await test.step('Expect setTheme set color', async () => {
await poLiveChat.page.evaluate(() => {
window.RocketChat.livechat.maximizeWidget();
window.RocketChat.livechat.setTheme({ color: 'rgb(50, 50, 50)' });
});
await expect(page.frameLocator('#rocketchat-iframe').locator('header')).toHaveCSS('background-color', 'rgb(50, 50, 50)');
});
await test.step('Expect setTheme set fontColor', async () => {
await poLiveChat.page.evaluate(() => {
window.RocketChat.livechat.maximizeWidget();
window.RocketChat.livechat.setTheme({ fontColor: 'rgb(50, 50, 50)' });
});
await expect(page.frameLocator('#rocketchat-iframe').locator('header')).toHaveCSS('color', 'rgb(50, 50, 50)');
});
await test.step('expect setTheme set hideExpandChat', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(poLiveChat.btnExpandChat).toBeVisible();
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setTheme({ hideExpandChat: true }));
await expect(poLiveChat.btnExpandChat).not.toBeVisible();
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setTheme({ hideExpandChat: false }));
await expect(poLiveChat.btnExpandChat).toBeVisible();
});
// TODO: fix iconColor setTheme property
// await test.step('Expect setTheme set iconColor', async () => {
// await poLiveChat.page.evaluate(() => {
// window.RocketChat.livechat.maximizeWidget();
// window.RocketChat.livechat.setTheme({ iconColor: 'rgb(50, 50, 50)' });
// });
// await expect(page.frameLocator('#rocketchat-iframe').locator('header')).toHaveCSS('color', 'rgb(50, 50, 50)');
// });
await test.step('Expect setTheme set title', async () => {
await poLiveChat.page.evaluate(() => {
window.RocketChat.livechat.maximizeWidget();
window.RocketChat.livechat.setTheme({ title: 'CustomTitle' });
});
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(page.frameLocator('#rocketchat-iframe').getByText('CustomTitle')).toBeVisible();
});
// await test.step('Expect setTheme set offlineTitle', async () => {
// await poLiveChat.page.evaluate(() => {
// window.RocketChat.livechat.maximizeWidget();
// window.RocketChat.livechat.setTheme({ offlineTitle: 'CustomOfflineTitle' });
// });
// await expect(page.frameLocator('#rocketchat-iframe').getByText('CustomTitle')).toBeVisible();
// });
});
test.skip('OC - Livechat API - setParentUrl', async () => {
// TODO: check how to test this, not sure there is a clear indication of parent url changes
await test.step('Expect setParentUrl to do something', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setParentUrl('http://localhost:3000'));
});
});
});
test.describe('Complex Widget Interactions', () => {
// Needs Departments to test this, so needs an EE license for multiple deps
test.skip(!IS_EE, 'Enterprise Only');
// Tests that requires interaction from an agent or more
let poAuxContext: { page: Page; poHomeOmnichannel: HomeOmnichannel };
let poLiveChat: OmnichannelLiveChatEmbedded;
let page: Page;
let agent: Awaited<ReturnType<typeof createAgent>>;
let agent2: Awaited<ReturnType<typeof createAgent>>;
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let pageContext: Page;
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
agent2 = await createAgent(api, 'user2');
departments = await Promise.all([createDepartment(api), createDepartment(api)]);
const [departmentA, departmentB] = departments.map(({ data }) => data);
await addAgentToDepartment(api, { department: departmentA, agentId: agent.data._id });
await addAgentToDepartment(api, { department: departmentB, agentId: agent2.data._id });
expect((await api.post('/settings/Livechat_offline_email', { value: 'test@testing.com' })).status()).toBe(200);
await api.post('/settings/Livechat_enabled_when_agent_idle', { value: false });
});
test.beforeEach(async ({ browser }, testInfo) => {
page = await browser.newPage();
poLiveChat = new OmnichannelLiveChatEmbedded(page);
const { page: pageCtx } = await createAuxContext(browser, Users.user1);
poAuxContext = { page: pageCtx, poHomeOmnichannel: new HomeOmnichannel(pageCtx) };
// This is needed since the livechat will not react to online/offline status changes if already loaded in a page
if (testInfo.title === 'Expect onOfflineFormSubmit to trigger callback') {
await poAuxContext.poHomeOmnichannel.navbar.changeUserStatus('offline');
} else {
await poAuxContext.poHomeOmnichannel.navbar.changeUserStatus('online');
}
await poLiveChat.goto();
});
test.afterEach(async () => {
await Promise.all([poAuxContext.page.close(), page.close(), pageContext?.close()]);
});
test.afterAll(async ({ api }) => {
await agent.delete();
await agent2.delete();
await expect((await api.post('/settings/Omnichannel_enable_department_removal', { value: true })).status()).toBe(200);
await Promise.all([...departments.map((department) => department.delete())]);
await expect((await api.post('/settings/Omnichannel_enable_department_removal', { value: false })).status()).toBe(200);
await api.post('/settings/Livechat_enabled_when_agent_idle', { value: true });
});
// clearBusinessUnit
// clearDepartment
// initialize
// maximizeWidget
// minimizeWidget
// pageVisited
// registerGuest
// setAgent
// setBusinessUnit
// setCustomField
// setDepartment
// setGuestEmail
// setGuestName
// setGuestToken
// setParentUrl
// setTheme
test.skip('OC - Livechat API - clearBusinessUnit', async () => {
// TODO: check how to test this, and if this is working as intended
await test.step('Expect clearBusinessUnit to do something', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.clearBusinessUnit());
});
});
test.skip('OC - Livechat API - setBusinessUnit', async () => {
// TODO
await test.step('Expect setBusinessUnit to do something', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setBusinessUnit());
});
});
test.skip('OC - Livechat API - setCustomField', async () => {
// TODO
await test.step('Expect setCustomField to do something', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setCustomField({ key: 'test', value: 'test' }));
});
});
test.skip('OC - Livechat API - clearDepartment', async () => {
// TODO
await test.step('Expect clearDepartment to do something', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.clearDepartment());
});
});
test.describe('OC - Livechat API - setDepartment', () => {
let poAuxContext2: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeEach(async ({ browser }) => {
const { page: pageCtx2 } = await createAuxContext(browser, Users.user2);
poAuxContext2 = { page: pageCtx2, poHomeOmnichannel: new HomeOmnichannel(pageCtx2) };
});
test.afterEach(async () => {
await poAuxContext2.page.close();
});
test('setDepartment - Called during ongoing conversation', async () => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
const registerGuestVisitor = createFakeVisitorRegistration({
department: departmentA._id,
});
// Start Chat
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await test.step('Expect registered guest to be in dep1', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(registerGuestVisitor.name);
await expect(poAuxContext.poHomeOmnichannel.content.channelHeader).toContainText(registerGuestVisitor.name);
});
const depId = departmentB._id;
await test.step('Expect chat not be transferred', async () => {
await poLiveChat.page.evaluate((depId) => window.RocketChat.livechat.setDepartment(depId), depId);
await poAuxContext2.poHomeOmnichannel.navbar.searchInput.click();
await poAuxContext2.poHomeOmnichannel.navbar.typeSearch(registerGuestVisitor.name);
await expect(poAuxContext2.poHomeOmnichannel.navbar.getSearchRoomByName(registerGuestVisitor.name)).not.toBeVisible();
});
await test.step('Expect registered guest to still be in dep1', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(registerGuestVisitor.name);
await expect(poAuxContext.poHomeOmnichannel.content.channelHeader).toContainText(registerGuestVisitor.name);
});
});
test('setDepartment - Called before conversation', async () => {
const departmentB = departments[1].data;
const registerGuestVisitor = createFakeVisitor();
const depId = departmentB._id;
await poLiveChat.page.evaluate((depId) => window.RocketChat.livechat.setDepartment(depId), depId);
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await poLiveChat.sendMessage(registerGuestVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await test.step('Expect registered guest to be in dep2', async () => {
await poAuxContext2.poHomeOmnichannel.navbar.openChat(registerGuestVisitor.name);
await poAuxContext2.page.locator('role=main').waitFor();
await poAuxContext2.page.locator('role=main >> role=heading[level=1]').waitFor();
await expect(poAuxContext2.page.locator('role=main >> .rcx-skeleton')).toHaveCount(0);
await expect(poAuxContext2.page.locator('role=main >> role=list')).not.toHaveAttribute('aria-busy', 'true');
});
await test.step('Expect registered guest not to be in dep1', async () => {
await poAuxContext.poHomeOmnichannel.navbar.typeSearch(registerGuestVisitor.name);
await expect(poAuxContext.poHomeOmnichannel.navbar.getSearchRoomByName(registerGuestVisitor.name)).not.toBeVisible();
});
});
});
test.describe('OC - Livechat API - transferChat', () => {
let poAuxContext2: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeEach(async ({ browser }) => {
const { page: pageCtx2 } = await createAuxContext(browser, Users.user2);
poAuxContext2 = { page: pageCtx2, poHomeOmnichannel: new HomeOmnichannel(pageCtx2) };
});
test.afterEach(async () => {
await poAuxContext2.page.close();
});
test('transferChat - Called during ongoing conversation', async () => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
const registerGuestVisitor = createFakeVisitorRegistration({
department: departmentA._id,
});
// Start Chat
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await test.step('Expect registered guest to be in dep1', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(registerGuestVisitor.name);
await expect(poAuxContext.poHomeOmnichannel.content.channelHeader).toContainText(registerGuestVisitor.name);
});
const depId = departmentB._id;
await test.step('Expect chat to be transferred', async () => {
await poLiveChat.page.evaluate((depId) => window.RocketChat.livechat.transferChat(depId), depId);
await poAuxContext2.poHomeOmnichannel.navbar.openChat(registerGuestVisitor.name);
await poAuxContext2.poHomeOmnichannel.navbar.typeSearch(registerGuestVisitor.name);
await expect(poAuxContext2.poHomeOmnichannel.navbar.getSearchRoomByName(registerGuestVisitor.name)).toBeVisible();
});
});
});
test('OC - Livechat API - registerGuest', async ({ browser }) => {
const registerGuestVisitor = createFakeVisitorRegistration();
await test.step('Expect registerGuest to create a valid guest', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect registered guest to have valid info', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(registerGuestVisitor.name);
await poAuxContext.poHomeOmnichannel.roomToolbar.openContactInfo();
// For some reason the guest info email information is being set to lowercase
await expect(poAuxContext.poHomeOmnichannel.contacts.contactInfo.infoContactEmail).toHaveText(
registerGuestVisitor.email.toLowerCase(),
);
});
await test.step('Expect registerGuest to log in an existing guest and load chat history', async () => {
({ page: pageContext } = await createAuxContext(browser, Users.user1));
await new OmnichannelLiveChatEmbedded(pageContext).goto();
await pageContext.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(pageContext.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await pageContext.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(pageContext.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await expect(pageContext.frameLocator('#rocketchat-iframe').getByText('this_a_test_message_from_visitor')).toBeVisible();
});
});
test('OC - Livechat API - registerGuest different guests', async () => {
const registerGuestVisitor1 = createFakeVisitorRegistration();
const registerGuestVisitor2 = createFakeVisitorRegistration();
await test.step('Expect registerGuest to create guest 1', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(poLiveChat.page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await poLiveChat.page.evaluate(
(registerGuestVisitor1) => window.RocketChat.livechat.registerGuest(registerGuestVisitor1),
registerGuestVisitor1,
);
await expect(poLiveChat.page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor_1');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_visitor_1')).toBeVisible();
await poAuxContext.poHomeOmnichannel.navbar.openChat(registerGuestVisitor1.name);
await poAuxContext.poHomeOmnichannel.content.sendMessage('this_is_a_test_message_from_agent');
await expect(poLiveChat.txtChatMessage('this_is_a_test_message_from_agent')).toBeVisible();
});
await test.step('Expect registerGuest to create guest 2', async () => {
await poLiveChat.page.evaluate(
(registerGuestVisitor2) => window.RocketChat.livechat.registerGuest(registerGuestVisitor2),
registerGuestVisitor2,
);
// wait for load messages to happen
await page.waitForResponse((response) => response.url().includes(`token=${registerGuestVisitor2.token}`));
await poLiveChat.page
.frameLocator('#rocketchat-iframe')
.getByText('this_a_test_message_from_visitor_1')
.waitFor({ state: 'hidden' });
await expect(poLiveChat.page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor_2');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await poLiveChat.txtChatMessage('this_a_test_message_from_visitor_2').waitFor({ state: 'visible' });
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_visitor_2')).toBeVisible();
});
});
test('OC - Livechat API - registerGuest multiple times', async () => {
const registerGuestVisitor = createFakeVisitorRegistration();
await test.step('Expect registerGuest work with the same token, multiple times', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_visitor')).toBeVisible();
await poLiveChat.page.evaluate((registerGuestVisitor) => {
window.RocketChat.livechat.registerGuest(registerGuestVisitor);
window.RocketChat.livechat.registerGuest(registerGuestVisitor);
}, registerGuestVisitor);
await page.waitForResponse('**/api/v1/livechat/visitor');
await page.waitForTimeout(500); // NOTE: timeout is necessary to allow websocket unsubscribes to happen
await poLiveChat.onlineAgentMessage.fill('this_a_new_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_visitor')).toBeVisible();
await expect(poLiveChat.txtChatMessage('this_a_new_test_message_from_visitor')).toBeVisible();
});
});
test.skip('OC - Livechat API - setGuestEmail', async () => {
const registerGuestVisitor = createFakeVisitorRegistration();
// Start Chat
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await test.step('Expect setGuestEmail to change a guest email', async () => {
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.setGuestEmail(`changed${registerGuestVisitor.email}`),
registerGuestVisitor,
);
});
await test.step('Expect registered guest to have valid info', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(registerGuestVisitor.name);
await poAuxContext.poHomeOmnichannel.roomToolbar.openContactInfo();
// For some reason the guest info email information is being set to lowercase
await expect(poAuxContext.poHomeOmnichannel.contacts.contactInfo.infoContactEmail).toHaveText(
`changed${registerGuestVisitor.email}`.toLowerCase(),
);
});
});
test('OC - Livechat API - setGuestName', async () => {
const registerGuestVisitor = createFakeVisitorRegistration();
// Start Chat
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await poAuxContext.poHomeOmnichannel.navbar.openChat(registerGuestVisitor.name);
await test.step('Expect setGuestEmail to change a guest email', async () => {
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.setGuestName(`changed${registerGuestVisitor.name}`),
registerGuestVisitor,
);
});
await test.step('Expect registered guest to have valid info', async () => {
await expect(poAuxContext.poHomeOmnichannel.content.infoHeaderName).toContainText(`changed${registerGuestVisitor.name}`);
});
});
test('OC - Livechat API - setGuestToken', async ({ browser }) => {
const registerGuestVisitor = createFakeVisitorRegistration();
// Register guest and send a message
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await poLiveChat.page.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.registerGuest(registerGuestVisitor),
registerGuestVisitor,
);
await expect(page.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await test.step('Expect setGuestToken to log in an existing guest and load chat history', async () => {
({ page: pageContext } = await createAuxContext(browser, Users.user1));
await new OmnichannelLiveChatEmbedded(pageContext).goto();
await pageContext.evaluate(() => window.RocketChat.livechat.maximizeWidget());
await expect(pageContext.frameLocator('#rocketchat-iframe').getByText('Start Chat')).toBeVisible();
await pageContext.evaluate(
(registerGuestVisitor) => window.RocketChat.livechat.setGuestToken(registerGuestVisitor.token),
registerGuestVisitor,
);
await expect(pageContext.frameLocator('#rocketchat-iframe').getByText('Start Chat')).not.toBeVisible();
await expect(pageContext.frameLocator('#rocketchat-iframe').getByText('this_a_test_message_from_visitor')).toBeVisible();
});
});
});
test.describe('Widget Listeners', () => {
// Tests that listen to events from the widget, and check if they are being triggered
let poAuxContext: { page: Page; poHomeOmnichannel: HomeOmnichannel };
let poLiveChat: OmnichannelLiveChatEmbedded;
let page: Page;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
expect((await api.post('/settings/Livechat_offline_email', { value: 'test@testing.com' })).status()).toBe(200);
await api.post('/settings/Livechat_enabled_when_agent_idle', { value: false });
});
test.beforeEach(async ({ browser }, testInfo) => {
page = await browser.newPage();
poLiveChat = new OmnichannelLiveChatEmbedded(page);
const { page: pageCtx } = await createAuxContext(browser, Users.user1);
poAuxContext = { page: pageCtx, poHomeOmnichannel: new HomeOmnichannel(pageCtx) };
// This is needed since the livechat will not react to online/offline status changes if already loaded in a page
if (testInfo.title === 'Expect onOfflineFormSubmit to trigger callback') {
await poAuxContext.poHomeOmnichannel.navbar.changeUserStatus('offline');
} else {
await poAuxContext.poHomeOmnichannel.navbar.changeUserStatus('online');
}
await poLiveChat.goto();
});
test.afterEach(async () => {
await poAuxContext.page.close();
await page.close();
});
test.afterAll(async ({ api }) => {
await agent.delete();
await api.post('/settings/Livechat_enabled_when_agent_idle', { value: true });
});
test('OC - Livechat API - onChatMaximized & onChatMinimized', async () => {
await test.step('Expect onChatMaximized to trigger callback', async () => {
await poLiveChat.page.evaluate(
() =>
new Promise((resolve: (value?: unknown) => void) => {
window.RocketChat.livechat.onChatMaximized(() => {
resolve();
});
window.RocketChat.livechat.maximizeWidget();
}),
);
});
await test.step('Expect onChatMinimized to trigger callback', async () => {
await poLiveChat.page.evaluate(
() =>
new Promise((resolve: (value?: unknown) => void) => {
window.RocketChat.livechat.onChatMinimized(() => {
resolve();
});
window.RocketChat.livechat.minimizeWidget();
}),
);
});
});
test('OC - Livechat API - onChatStarted & onChatEnded', async () => {
const newVisitor = createFakeVisitor();
await test.step('Expect onChatStarted to trigger callback', async () => {
const watchForTrigger = page.waitForFunction(() => window.onChatStarted === true);
await poLiveChat.page.evaluate(() =>
window.RocketChat.livechat.onChatStarted(() => {
window.onChatStarted = true;
}),
);
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await watchForTrigger;
});
await test.step('Expect onChatEnded to trigger callback', async () => {
const watchForTrigger = page.waitForFunction(() => window.onChatEnded === true);
await poLiveChat.page.evaluate(() =>
window.RocketChat.livechat.onChatEnded(() => {
window.onChatEnded = true;
}),
);
await poAuxContext.poHomeOmnichannel.navbar.openChat(newVisitor.name);
await poAuxContext.poHomeOmnichannel.quickActionsRoomToolbar.closeChat({ comment: 'this_is_a_test_comment' });
await watchForTrigger;
});
});
test('OC - Livechat API - onPrechatFormSubmit & onAssignAgent', async () => {
const newVisitor = createFakeVisitor();
await test.step('Expect onPrechatFormSubmit to trigger callback', async () => {
const watchForTrigger = page.waitForFunction(() => window.onPrechatFormSubmit === true);
await poLiveChat.page.evaluate(() =>
window.RocketChat.livechat.onPrechatFormSubmit(() => {
window.onPrechatFormSubmit = true;
}),
);
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await watchForTrigger;
});
await test.step('Expect onAssignAgent to trigger callback', async () => {
const watchForTrigger = page.waitForFunction(() => window.onAssignAgent === true);
await poLiveChat.page.evaluate(() =>
window.RocketChat.livechat.onAssignAgent(() => {
window.onAssignAgent = true;
}),
);
await poLiveChat.btnSendMessageToOnlineAgent.click();
await watchForTrigger;
});
});
// TODO: Fix this Flaky test
test.skip('onAgentStatusChange', async () => {
const newVisitor = createFakeVisitor();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
const watchForTrigger = page.waitForFunction(() => window.onAgentStatusChange === true);
await poLiveChat.page.evaluate(() =>
window.RocketChat.livechat.onAgentStatusChange(() => {
window.onAgentStatusChange = true;
}),
);
await poAuxContext.poHomeOmnichannel.navbar.openChat(newVisitor.name);
await poAuxContext.poHomeOmnichannel.navbar.changeUserStatus('offline');
await watchForTrigger;
});
test('OC - Livechat API - onOfflineFormSubmit', async () => {
const newVisitor = createFakeVisitor();
await poAuxContext.poHomeOmnichannel.navbar.changeUserStatus('offline');
await poAuxContext.poHomeOmnichannel.navbar.switchOmnichannelStatus('offline');
const watchForTrigger = page.waitForFunction(() => window.onOfflineFormSubmit === true);
await poLiveChat.page.reload();
await poLiveChat.page.evaluate(() =>
window.RocketChat.livechat.onOfflineFormSubmit(() => {
window.onOfflineFormSubmit = true;
}),
);
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, true);
await watchForTrigger;
});
test('OC - Livechat API - onWidgetHidden & onWidgetShown', async () => {
await test.step('Expect onWidgetHidden to trigger callback', async () => {
await poLiveChat.page.evaluate(
() =>
new Promise((resolve: (value?: unknown) => void) => {
window.RocketChat.livechat.onWidgetHidden(() => {
resolve();
});
window.RocketChat.livechat.hideWidget();
}),
);
});
await test.step('Expect onWidgetShown to trigger callback', async () => {
await poLiveChat.page.evaluate(
() =>
new Promise((resolve: (value?: unknown) => void) => {
window.RocketChat.livechat.onWidgetShown(() => {
resolve();
});
window.RocketChat.livechat.showWidget();
}),
);
});
});
test.skip('OC - Livechat API - onServiceOffline', async () => {
// TODO: Not sure how to test this, need to check if playwright has a way to mock a server disconnect
await test.step('Expect onServiceOffline to do something', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.onServiceOffline(() => console.log('onServiceOffline')));
});
});
test.skip('OC - Livechat API - onQueuePositionChange', async () => {
// TODO
await test.step('Expect onQueuePositionChange to do something', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.onQueuePositionChange(() => console.log('onQueuePositionChange')));
});
});
});
});

View File

@@ -0,0 +1,96 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChatEmbedded } from '../page-objects/omnichannel';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
declare const window: Window & {
RocketChat: {
livechat: {
setTheme: (theme: { hideAgentAvatar?: boolean; hideGuestAvatar?: boolean }) => void;
};
};
};
test.use({ storageState: Users.user1.state });
test.describe('OC - Livechat - Avatar visibility', async () => {
let agent: Awaited<ReturnType<typeof createAgent>>;
let poLiveChat: OmnichannelLiveChatEmbedded;
let poAuxContext: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
const res = await makeAgentAvailable(api, agent.data._id);
if (res.status() !== 200) {
throw new Error('Failed to make agent available');
}
});
test.beforeEach(async ({ browser }) => {
const { page: pageCtx } = await createAuxContext(browser, Users.user1);
poAuxContext = { page: pageCtx, poHomeOmnichannel: new HomeOmnichannel(pageCtx) };
});
test.beforeEach(async ({ page }) => {
poLiveChat = new OmnichannelLiveChatEmbedded(page);
await poLiveChat.goto();
});
test.afterEach(async ({ page }) => {
await poAuxContext.page.close();
await page.close();
});
test.afterAll(async () => {
await agent.delete();
});
test('OC - Livechat - Change avatar visibility', async () => {
const visitor = createFakeVisitor();
await test.step('should initiate Livechat conversation', async () => {
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(visitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_user')).toBeVisible();
});
await test.step('expect to send a message as agent', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(visitor.name);
await poAuxContext.poHomeOmnichannel.content.sendMessage('this_is_a_test_message_from_agent');
await expect(poLiveChat.txtChatMessage('this_is_a_test_message_from_agent')).toBeVisible();
});
await test.step('expect visitor avatar to be hidden', async () => {
await expect(poLiveChat.imgAvatar(visitor.name)).not.toBeVisible();
});
await test.step('expect agent avatar to be visible', async () => {
await expect(poLiveChat.imgAvatar('user1')).toBeVisible();
});
await test.step('expect to make visitor avatar visible', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setTheme({ hideGuestAvatar: false }));
await expect(poLiveChat.imgAvatar(visitor.name)).toBeVisible();
});
await test.step('expect to hide agent avatar', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setTheme({ hideAgentAvatar: false }));
await expect(poLiveChat.imgAvatar('user1')).toBeVisible();
});
await test.step('should close the conversation', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(visitor.name);
await poAuxContext.poHomeOmnichannel.quickActionsRoomToolbar.closeChat({ comment: 'this_is_a_test_comment' });
});
});
});

View File

@@ -0,0 +1,99 @@
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelLiveChatEmbedded } from '../page-objects/omnichannel';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
declare const window: Window & {
RocketChat: {
livechat: {
setTheme: (theme: { background?: string }) => void;
};
};
};
test.use({ storageState: Users.admin.state });
test.skip(!IS_EE, 'Enterprise Only');
test.describe('OC - Livechat - Message list background', async () => {
let agent: Awaited<ReturnType<typeof createAgent>>;
let poLiveChat: OmnichannelLiveChatEmbedded;
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
const res = await makeAgentAvailable(api, agent.data._id);
if (res.status() !== 200) {
throw new Error('Failed to make agent available');
}
});
test.beforeEach(async ({ page }) => {
poLiveChat = new OmnichannelLiveChatEmbedded(page);
await poLiveChat.goto();
});
test.afterEach(async ({ page }) => {
await page.close();
});
test.afterAll(async () => {
await agent.delete();
});
test('OC - Livechat - Change message list background', async ({ api, page }) => {
const visitor = createFakeVisitor();
await test.step('should initiate Livechat conversation', async () => {
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(visitor, false);
await poLiveChat.onlineAgentMessage.fill('message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('message_from_user')).toBeVisible();
});
await test.step('expect message list to have default background', async () => {
await expect(await poLiveChat.messageListBackground).toBe('rgba(0, 0, 0, 0)');
});
await test.step('expect to change message list background', async () => {
const res = await api.post('/settings/Livechat_background', { value: 'rgb(186, 1, 85)' });
await expect(res.status()).toBe(200);
await page.reload();
await poLiveChat.openLiveChat();
await expect(await poLiveChat.messageListBackground).toBe('rgb(186, 1, 85)');
});
await test.step('expect to give priority to background provided via api', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setTheme({ background: 'rgb(186, 218, 85)' }));
await expect(await poLiveChat.messageListBackground).toBe('rgb(186, 218, 85)');
});
await test.step('expect to fallback to setting if api background is not available', async () => {
await poLiveChat.page.evaluate(() => window.RocketChat.livechat.setTheme({ background: undefined }));
await expect(await poLiveChat.messageListBackground).toBe('rgb(186, 1, 85)');
});
await test.step('expect to reset message list background to default', async () => {
const res = await api.post('/settings/Livechat_background', { value: '' });
await expect(res.status()).toBe(200);
await page.reload();
await poLiveChat.openLiveChat();
await expect(await poLiveChat.messageListBackground).toBe('rgba(0, 0, 0, 0)');
});
await test.step('should close the conversation', async () => {
await poLiveChat.btnOptions.click();
await poLiveChat.btnCloseChat.click();
await poLiveChat.btnCloseChatConfirm.click();
await expect(poLiveChat.btnNewChat).toBeVisible();
});
});
});

View File

@@ -0,0 +1,153 @@
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { createAgent } from '../utils/omnichannel/agents';
import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.user1.state });
test.describe('OC - Livechat - Department Flow', () => {
// Needs Departments to test this, so needs an EE license for multiple deps
test.skip(!IS_EE, 'Enterprise Only');
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannelAgent1: HomeOmnichannel;
let poHomeOmnichannelAgent2: HomeOmnichannel;
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let departmentA: Awaited<ReturnType<typeof createDepartment>>['data'];
let departmentB: Awaited<ReturnType<typeof createDepartment>>['data'];
let agents: Awaited<ReturnType<typeof createDepartment>>[];
let agent1: Awaited<ReturnType<typeof createAgent>>['data'];
let agent2: Awaited<ReturnType<typeof createAgent>>['data'];
test.beforeAll(async ({ api }) => {
// Assign agents & departments
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]);
[agent1, agent2] = agents.map(({ data }) => data);
departments = await Promise.all([
createDepartment(api, { showOnRegistration: true }),
createDepartment(api, { showOnRegistration: true }),
]);
[departmentA, departmentB] = departments.map(({ data }) => data);
await addAgentToDepartment(api, { department: departmentA, agentId: agent1._id });
await addAgentToDepartment(api, { department: departmentB, agentId: agent2._id });
});
test.beforeEach(async ({ page, api, browser }) => {
// Create Pages
const { page: agent1Page } = await createAuxContext(browser, Users.user1, '/', true);
poHomeOmnichannelAgent1 = new HomeOmnichannel(agent1Page);
const { page: agent2Page } = await createAuxContext(browser, Users.user2, '/', true);
poHomeOmnichannelAgent2 = new HomeOmnichannel(agent2Page);
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
});
test.afterEach(async ({ page }) => {
await poHomeOmnichannelAgent1.page.close();
await poHomeOmnichannelAgent2.page.close();
await page.close();
});
test.afterAll(async ({ api }) => {
await expect((await api.post('/settings/Omnichannel_enable_department_removal', { value: true })).status()).toBe(200);
await Promise.all([...agents.map((agent) => agent.delete())]);
await Promise.all([...departments.map((department) => department.delete())]);
await expect((await api.post('/settings/Omnichannel_enable_department_removal', { value: false })).status()).toBe(200);
});
test('OC - Livechat - Chat with Department', async () => {
const guest = createFakeVisitor();
await test.step('expect start Chat with department', async () => {
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(guest, false, departmentA.name);
await expect(poLiveChat.onlineAgentMessage).toBeVisible();
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_user"')).toBeVisible();
});
await test.step('expect message to be received by department', async () => {
await poHomeOmnichannelAgent1.navbar.openChat(guest.name);
await expect(poHomeOmnichannelAgent1.content.lastUserMessage).toBeVisible();
await expect(poHomeOmnichannelAgent1.content.lastUserMessage).toContainText('this_a_test_message_from_user');
});
await test.step('expect message to be sent by department', async () => {
await poHomeOmnichannelAgent1.content.sendMessage('this_a_test_message_from_agent');
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_agent"')).toBeVisible();
});
});
test('OC - Livechat - Change Department', async () => {
const guest = createFakeVisitor();
await test.step('expect start Chat with department', async () => {
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(guest, false, departmentA.name);
await expect(poLiveChat.onlineAgentMessage).toBeVisible();
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_user"')).toBeVisible();
});
await test.step('expect message to be received by department 1', async () => {
await poHomeOmnichannelAgent1.navbar.openChat(guest.name);
await expect(poHomeOmnichannelAgent1.content.lastUserMessage).toBeVisible();
await expect(poHomeOmnichannelAgent1.content.lastUserMessage).toContainText('this_a_test_message_from_user');
});
await test.step('expect message to be sent by department 1', async () => {
await poHomeOmnichannelAgent1.content.sendMessage('this_a_test_message_from_agent_department_1');
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_agent_department_1"')).toBeVisible();
await poHomeOmnichannelAgent1.page.close();
});
await test.step('expect to change department', async () => {
await poLiveChat.btnOptions.click();
await poLiveChat.btnChangeDepartment.click();
await expect(poLiveChat.selectDepartment).toBeVisible();
await poLiveChat.selectDepartment.selectOption({ label: departmentB.name });
await expect(poLiveChat.btnSendMessage('Start chat')).toBeEnabled();
await poLiveChat.btnSendMessage('Start chat').click();
await expect(poLiveChat.livechatModal).toBeVisible();
await expect(poLiveChat.livechatModalText('Are you sure you want to switch the department?')).toBeVisible();
await poLiveChat.btnYes.click();
await expect(poLiveChat.livechatModal).toBeVisible();
await expect(poLiveChat.livechatModalText('Department switched')).toBeVisible();
await poLiveChat.btnOk.click();
// Expect keep chat history
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_user"')).toBeVisible();
// Expect user to have changed
await expect(await poLiveChat.headerTitle.textContent()).toEqual(agent2.username);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user_to_department_2');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_user_to_department_2"')).toBeVisible();
});
await test.step('expect message to be received by department', async () => {
await poHomeOmnichannelAgent2.navbar.openChat(guest.name);
await expect(poHomeOmnichannelAgent2.content.lastUserMessage).toBeVisible();
await expect(poHomeOmnichannelAgent2.content.lastUserMessage).toContainText('this_a_test_message_from_user_to_department_2');
});
await test.step('expect message to be sent by department', async () => {
await poHomeOmnichannelAgent2.content.sendMessage('this_a_test_message_from_agent_department_2');
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_agent_department_2"')).toBeVisible();
});
});
});

View File

@@ -0,0 +1,124 @@
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { createAgent } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
const visitor = createFakeVisitor();
// Endpoint defaults are reset after each test, so if not in matrix assume is true
const endpointMatrix = [
[{ url: '/settings/FileUpload_Enabled', value: false }],
[{ url: '/settings/Livechat_fileupload_enabled', value: false }],
[
{ url: '/settings/FileUpload_Enabled', value: false },
{ url: '/settings/Livechat_fileupload_enabled', value: false },
],
];
const beforeTest = async (poLiveChat: OmnichannelLiveChat) => {
await poLiveChat.goto();
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(visitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await poLiveChat.txtChatMessage('this_a_test_message_from_user').waitFor({ state: 'visible' });
};
test.describe('OC - Livechat - OC - File Upload', () => {
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannel: HomeOmnichannel;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ browser, api }) => {
agent = await createAgent(api, 'user1');
const { page } = await createAuxContext(browser, Users.user1, '/');
poHomeOmnichannel = new HomeOmnichannel(page);
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterAll(async ({ api }) => {
await api.post('/settings/FileUpload_Enabled', { value: true });
await api.post('/settings/Livechat_fileupload_enabled', { value: true });
await poHomeOmnichannel.page.close();
await agent.delete();
});
// Default settings are FileUpload_Enabled true and Livechat_fileupload_enabled true
test('OC - Livechat - txt Drag & Drop', async () => {
await beforeTest(poLiveChat);
await test.step('expect to upload a txt file', async () => {
await poLiveChat.dragAndDropTxtFile();
await expect(poLiveChat.findUploadedFileLink('any_file.txt')).toBeVisible();
});
});
test('OC - Livechat - lst Drag & Drop', async () => {
await beforeTest(poLiveChat);
await test.step('expect to upload a lst file', async () => {
await poLiveChat.dragAndDropLstFile();
await expect(poLiveChat.findUploadedFileLink('lst-test.lst')).toBeVisible();
});
});
});
test.describe('OC - Livechat - OC - File Upload - Disabled', () => {
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannel: HomeOmnichannel;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ browser, api }) => {
agent = await createAgent(api, 'user1');
const { page } = await createAuxContext(browser, Users.user1, '/');
poHomeOmnichannel = new HomeOmnichannel(page);
});
test.afterAll(async ({ api }) => {
await api.post('/settings/FileUpload_Enabled', { value: true });
await api.post('/settings/Livechat_fileupload_enabled', { value: true });
await poHomeOmnichannel.page?.close();
await agent.delete();
});
endpointMatrix.forEach((endpoints) => {
const testName = endpoints.map((endpoint) => endpoint.url.split('/').pop()?.concat(`=${endpoint.value}`)).join(' ');
test(`OC - Livechat - txt Drag & Drop - ${testName}`, async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
await Promise.all(
endpoints.map(async (endpoint: { url: string; value: boolean }) => {
await api.post(endpoint.url, { value: endpoint.value });
}),
);
await poLiveChat.goto();
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(visitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await poLiveChat.txtChatMessage('this_a_test_message_from_user').waitFor({ state: 'visible' });
await test.step('expect to upload a txt file', async () => {
await poLiveChat.dragAndDropTxtFile();
await expect(poLiveChat.alertMessage('File upload is disabled')).toBeVisible();
});
});
});
});

View File

@@ -0,0 +1,66 @@
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { OmnichannelLiveChat, OmnichannelLivechatAppearance } from '../page-objects/omnichannel';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
test.skip(!IS_EE, 'Enterprise Only');
test.use({ storageState: Users.admin.state });
test.describe('OC - Livechat - Hide "Expand chat"', async () => {
let agent: Awaited<ReturnType<typeof createAgent>>;
let poLiveChat: OmnichannelLiveChat;
let poLivechatAppearance: OmnichannelLivechatAppearance;
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
const res = await makeAgentAvailable(api, agent.data._id);
await expect(res.status()).toBe(200);
});
test.beforeEach(async ({ browser, api }) => {
const { page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false);
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
});
test.afterEach(async () => {
await poLiveChat.page.close();
});
test.beforeEach(async ({ page }) => {
poLivechatAppearance = new OmnichannelLivechatAppearance(page);
await poLivechatAppearance.goto();
});
test.afterAll(async ({ api }) => {
const res = await api.post('/settings/Livechat_hide_expand_chat', { value: false });
await expect(res.status()).toBe(200);
});
test('OC - Livechat - Hide "Expand chat"', async () => {
await test.step('expect to open Livechat', async () => {
await poLiveChat.openLiveChat();
});
await test.step('expect "Expand chat" button to start visible (default)', async () => {
await expect(poLiveChat.btnExpandChat).toBeVisible();
});
await test.step('expect to change setting', async () => {
await poLivechatAppearance.labelHideExpandChat.click();
await poLivechatAppearance.btnSaveChanges.click();
});
await test.step('expect "Expand chat" button to be hidden', async () => {
await poLiveChat.page.reload();
await poLiveChat.openLiveChat();
await expect(poLiveChat.btnExpandChat).toBeHidden();
});
});
});

View File

@@ -0,0 +1,63 @@
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { OmnichannelLiveChat, OmnichannelSettings } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.skip(!IS_EE, 'Enterprise Only');
test.use({ storageState: Users.admin.state });
test.describe('OC - Livechat - Widget logo', async () => {
let poLiveChat: OmnichannelLiveChat;
let poOmnichannelSettings: OmnichannelSettings;
test.beforeEach(async ({ browser, api }) => {
const { page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false);
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
});
test.afterEach(async () => {
await poLiveChat.page.close();
});
test.beforeEach(async ({ page }) => {
poOmnichannelSettings = new OmnichannelSettings(page);
await poOmnichannelSettings.goto();
});
test.afterAll(async ({ api }) => {
const res = await api.post('/assets.unsetAsset', { assetName: 'livechat_widget_logo' });
if (res.status() !== 200) {
throw new Error('Failed to unset asset');
}
});
test('OC - Livechat - Change widget logo', async () => {
await test.step('expect to have default logo', async () => {
await poOmnichannelSettings.group('Livechat').click();
await expect(poLiveChat.imgLogo).not.toBeVisible();
await expect(poOmnichannelSettings.imgLivechatLogoPreview).not.toBeVisible();
});
await test.step('expect to change widget logo', async () => {
await expect(poOmnichannelSettings.labelLivechatLogo).toHaveText('Livechat widget logo (svg, png, jpg)');
await poOmnichannelSettings.inputLivechatLogo.setInputFiles('./tests/e2e/fixtures/files/test-image.jpeg');
await expect(poOmnichannelSettings.imgLivechatLogoPreview).toBeVisible();
await poLiveChat.page.reload();
await expect(poLiveChat.imgLogo).toBeVisible();
await expect(poLiveChat.imgLogo).toHaveAttribute('src', 'assets/livechat_widget_logo.jpeg');
});
await test.step('expect to remove custom logo', async () => {
await poOmnichannelSettings.btnDeleteLivechatLogo.click();
await expect(poOmnichannelSettings.imgLivechatLogoPreview).not.toBeVisible();
await poLiveChat.page.reload();
await expect(poLiveChat.imgLogo).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,103 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChatEmbedded } from '../page-objects/omnichannel';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
declare const window: Window & {
RocketChat: {
livechat: {
setTheme: (theme: { guestBubbleBackgroundColor?: string; agentBubbleBackgroundColor?: string }) => void;
};
};
};
test.use({ storageState: Users.user1.state });
test.describe('OC - Livechat - Bubble background color', async () => {
let agent: Awaited<ReturnType<typeof createAgent>>;
let poLiveChat: OmnichannelLiveChatEmbedded;
let poAuxContext: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
const res = await makeAgentAvailable(api, agent.data._id);
if (res.status() !== 200) {
throw new Error('Failed to make agent available');
}
});
test.beforeEach(async ({ browser }) => {
const { page: pageCtx } = await createAuxContext(browser, Users.user1);
poAuxContext = { page: pageCtx, poHomeOmnichannel: new HomeOmnichannel(pageCtx) };
});
test.beforeEach(async ({ page }) => {
poLiveChat = new OmnichannelLiveChatEmbedded(page);
await poLiveChat.goto();
});
test.afterEach(async ({ page }) => {
await poAuxContext.page?.close();
await page.close();
});
test.afterAll(async () => {
await agent.delete();
});
test('OC - Livechat - Change bubble background color', async () => {
const visitor = createFakeVisitor();
await test.step('should initiate Livechat conversation', async () => {
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(visitor, false);
await poLiveChat.onlineAgentMessage.fill('message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('message_from_user')).toBeVisible();
});
await test.step('expect to send a message as agent', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(visitor.name);
await poAuxContext.poHomeOmnichannel.content.sendMessage('message_from_agent');
});
await test.step('expect to have default bubble background color', async () => {
expect(await poLiveChat.messageBubbleBackground('message_from_user')).toBe('rgb(193, 39, 45)');
expect(await poLiveChat.messageBubbleBackground('message_from_agent')).toBe('rgb(247, 248, 250)');
});
await test.step('expect to change bubble background color', async () => {
await poLiveChat.page.evaluate(() =>
window.RocketChat.livechat.setTheme({
guestBubbleBackgroundColor: 'rgb(186, 218, 85)',
agentBubbleBackgroundColor: 'rgb(0, 100, 250)',
}),
);
expect(await poLiveChat.messageBubbleBackground('message_from_user')).toBe('rgb(186, 218, 85)');
expect(await poLiveChat.messageBubbleBackground('message_from_agent')).toBe('rgb(0, 100, 250)');
});
await test.step('expect to reset bubble background color to defaults', async () => {
await poLiveChat.page.evaluate(() =>
window.RocketChat.livechat.setTheme({ guestBubbleBackgroundColor: undefined, agentBubbleBackgroundColor: undefined }),
);
expect(await poLiveChat.messageBubbleBackground('message_from_user')).toBe('rgb(193, 39, 45)');
expect(await poLiveChat.messageBubbleBackground('message_from_agent')).toBe('rgb(247, 248, 250)');
});
await test.step('should close the conversation', async () => {
await poAuxContext.poHomeOmnichannel.navbar.openChat(visitor.name);
await poAuxContext.poHomeOmnichannel.quickActionsRoomToolbar.closeChat({ comment: 'this_is_a_test_comment' });
});
});
});

View File

@@ -0,0 +1,124 @@
import type { BrowserContext } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
const firstVisitor = createFakeVisitor();
const secondVisitor = createFakeVisitor();
test.use({ storageState: Users.user1.state });
test.describe('OC - Livechat - Queue Management', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poHomeOmnichannel: HomeOmnichannel;
let poLiveChat: OmnichannelLiveChat;
let liveChatContext: BrowserContext;
const waitingQueueMessage = 'This is a message from Waiting Queue';
test.beforeAll(async ({ api, browser }) => {
await Promise.all([
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }),
api.post('/settings/Livechat_accept_chats_with_no_agents', { value: true }),
api.post('/settings/Livechat_waiting_queue', { value: true }),
api.post('/settings/Livechat_waiting_queue_message', { value: waitingQueueMessage }),
api.post('/livechat/users/agent', { username: 'user1' }),
]);
const { page: omniPage } = await createAuxContext(browser, Users.user1, '/', true);
poHomeOmnichannel = new HomeOmnichannel(omniPage);
// Agent will be offline for these tests
await poHomeOmnichannel.navbar.switchOmnichannelStatus('offline');
});
test.beforeEach(async ({ browser, api }) => {
liveChatContext = await browser.newContext();
const page2 = await liveChatContext.newPage();
poLiveChat = new OmnichannelLiveChat(page2, api);
await poLiveChat.goto();
});
test.afterAll(async ({ api }) => {
await Promise.all([
api.post('/settings/Livechat_accept_chats_with_no_agents', { value: false }),
api.post('/settings/Livechat_waiting_queue', { value: false }),
api.post('/settings/Livechat_waiting_queue_message', { value: '' }),
api.delete('/livechat/users/agent/user1'),
]);
await poHomeOmnichannel.page.close();
});
test.describe('OC - Queue Management - Auto Selection', () => {
let poLiveChat2: OmnichannelLiveChat;
let liveChat2Context: BrowserContext;
test.beforeEach(async ({ browser, api }) => {
liveChat2Context = await browser.newContext();
const page = await liveChat2Context.newPage();
poLiveChat2 = new OmnichannelLiveChat(page, api);
await poLiveChat2.goto();
});
test.afterEach(async () => {
await poLiveChat2.closeChat();
await liveChat2Context.close();
await poLiveChat.closeChat();
await liveChatContext.close();
});
test('Update user position on Queue', async () => {
await test.step('should start livechat session', async () => {
await poLiveChat.openAnyLiveChatAndSendMessage({
liveChatUser: firstVisitor,
message: 'Test message',
isOffline: false,
});
});
await test.step('expect to receive Waiting Queue message on chat', async () => {
await expect(poLiveChat.page.locator(`div >> text=${waitingQueueMessage}`)).toBeVisible();
});
await test.step('expect to be on spot #1', async () => {
await expect(poLiveChat.queuePosition(1)).toBeVisible();
});
await test.step('should start secondary livechat session', async () => {
await poLiveChat2.openAnyLiveChatAndSendMessage({
liveChatUser: secondVisitor,
message: 'Test message',
isOffline: false,
});
});
await test.step('should start secondary livechat on spot #2', async () => {
await expect(poLiveChat2.queuePosition(2)).toBeVisible();
});
await test.step('should start the queue by making the agent available again', async () => {
await poHomeOmnichannel.navbar.switchOmnichannelStatus('online');
});
await test.step('user1 should get assigned to the first chat', async () => {
await expect(poLiveChat.queuePosition(1)).not.toBeVisible();
});
await test.step('secondary session should be on position #1', async () => {
await expect(poLiveChat2.queuePosition(1)).toBeVisible();
});
await test.step('secondary session should be taken by user1', async () => {
await expect(poLiveChat2.queuePosition(1)).not.toBeVisible();
});
});
});
});

View File

@@ -0,0 +1,247 @@
import type { BrowserContext } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
const firstVisitor = createFakeVisitor();
const secondVisitor = createFakeVisitor();
test.use({ storageState: Users.user1.state });
test.describe('OC - Livechat - Queue Management', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poHomeOmnichannel: HomeOmnichannel;
let poLiveChat: OmnichannelLiveChat;
let liveChatContext: BrowserContext;
const waitingQueueMessage = 'This is a message from Waiting Queue';
const queuePosition1 = 'Your spot is #1';
const queuePosition2 = 'Your spot is #2';
test.beforeAll(async ({ api, browser }) => {
await Promise.all([
api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }),
api.post('/settings/Livechat_waiting_queue', { value: true }),
api.post('/settings/Livechat_waiting_queue_message', { value: waitingQueueMessage }),
api.post('/livechat/users/agent', { username: 'user1' }),
]);
const { page: omniPage } = await createAuxContext(browser, Users.user1, '/', true);
poHomeOmnichannel = new HomeOmnichannel(omniPage);
});
test.beforeEach(async ({ browser, api }) => {
liveChatContext = await browser.newContext();
const page2 = await liveChatContext.newPage();
poLiveChat = new OmnichannelLiveChat(page2, api);
await poLiveChat.goto();
});
test.afterAll(async ({ api }) => {
await Promise.all([
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }),
api.post('/settings/Livechat_waiting_queue', { value: false }),
api.post('/settings/Livechat_waiting_queue_message', { value: '' }),
api.delete('/livechat/users/agent/user1'),
]);
await poHomeOmnichannel.page.close();
});
test.afterEach(async () => {
await poLiveChat.closeChat();
await liveChatContext.close();
});
test('OC - Queue Management - Waiting Queue Message enabled', async () => {
await test.step('should start livechat session', async () => {
await poLiveChat.openAnyLiveChatAndSendMessage({
liveChatUser: firstVisitor,
message: 'Test message',
isOffline: false,
});
});
await test.step('expect to receive Waiting Queue message on chat', async () => {
await expect(poLiveChat.page.locator(`div >> text=${waitingQueueMessage}`)).toBeVisible();
});
});
test.describe('OC - Queue Management - Update Queue Position', () => {
let poLiveChat2: OmnichannelLiveChat;
let liveChat2Context: BrowserContext;
test.beforeEach(async ({ browser, api }) => {
liveChat2Context = await browser.newContext();
const page = await liveChat2Context.newPage();
poLiveChat2 = new OmnichannelLiveChat(page, api);
await poLiveChat2.goto();
});
test.afterEach(async () => {
await poLiveChat2.closeChat();
await liveChat2Context.close();
});
test('Update user position on Queue', async () => {
await test.step('should start secondary livechat session', async () => {
await poLiveChat2.openAnyLiveChatAndSendMessage({
liveChatUser: secondVisitor,
message: 'Test message',
isOffline: false,
});
});
await test.step('should start primary livechat session', async () => {
await poLiveChat.openAnyLiveChatAndSendMessage({
liveChatUser: firstVisitor,
message: 'Test message',
isOffline: false,
});
});
await test.step('should verify the queue position of the primary user', async () => {
await expect(poLiveChat.page.locator(`div[role='alert'] >> text=${queuePosition2}`)).toBeVisible();
});
await test.step('should allow the agent to take the secondary user chat', async () => {
await poHomeOmnichannel.sidebar.getSidebarItemByName(secondVisitor.name).click();
await expect(poHomeOmnichannel.content.btnTakeChat).toBeVisible();
await poHomeOmnichannel.content.btnTakeChat.click();
await expect(poHomeOmnichannel.content.lastSystemMessageBody).toHaveText('joined the channel');
});
await test.step('expect the queue position of the primary user to update after the secondary users chat is taken', async () => {
await expect(poLiveChat.page.locator(`div[role='alert'] >> text=${queuePosition1}`)).toBeVisible();
});
});
});
});
test.describe('OC - Contact Manager Routing', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poHomeOmnichannel: HomeOmnichannel;
let poLiveChat: OmnichannelLiveChat;
let liveChatContext: BrowserContext;
// User2 will be the contact manager
let poHomeOmnichannelUser2: HomeOmnichannel;
const visitorWithManager = createFakeVisitor();
const contactId = `contact-${Date.now()}`;
test.beforeAll(async ({ api, browser }) => {
await api.post('/livechat/users/agent', { username: 'user2' });
await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' });
await api.post('/omnichannel/contact', {
_id: contactId,
name: visitorWithManager.name,
email: visitorWithManager.email,
contactManager: {
username: 'user2',
},
});
const { page: omniPage } = await createAuxContext(browser, Users.user1, '/', true);
poHomeOmnichannel = new HomeOmnichannel(omniPage);
const { page: omniPageUser2 } = await createAuxContext(browser, Users.user2, '/', true);
poHomeOmnichannelUser2 = new HomeOmnichannel(omniPageUser2);
});
test.beforeEach(async ({ browser, api }) => {
liveChatContext = await browser.newContext();
const page = await liveChatContext.newPage();
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
});
test.afterAll(async ({ api }) => {
await Promise.all([
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }),
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/agent/user2'),
api.delete(`/omnichannel/contact/${contactId}`),
]);
await poHomeOmnichannel.page.close();
await poHomeOmnichannelUser2.page.close();
});
test.afterEach(async () => {
await poLiveChat.closeChat();
await liveChatContext.close();
});
test('should route inquiry only to the contact manager', async () => {
await test.step('visitor with contact manager starts a chat', async () => {
await poLiveChat.openAnyLiveChatAndSendMessage({
liveChatUser: visitorWithManager,
message: 'I need assistance',
isOffline: false,
});
});
await test.step('verify non-manager agent does not see the inquiry', async () => {
const nonManagerQueuedChat = poHomeOmnichannel.sidebar.getSidebarItemByName(visitorWithManager.name);
await expect(nonManagerQueuedChat).toHaveCount(0);
});
await test.step('verify the contact manager agent sees the inquiry', async () => {
const managerQueuedChat = poHomeOmnichannelUser2.sidebar.getSidebarItemByName(visitorWithManager.name);
await expect(managerQueuedChat).toBeVisible();
});
await test.step('contact manager can take the chat', async () => {
await poHomeOmnichannelUser2.sidebar.getSidebarItemByName(visitorWithManager.name).click();
await expect(poHomeOmnichannelUser2.content.btnTakeChat).toBeVisible();
await poHomeOmnichannelUser2.content.btnTakeChat.click();
await expect(poHomeOmnichannelUser2.content.lastSystemMessageBody).toHaveText('joined the channel');
});
});
test('inquiry should persist only in contact manager queue after page refresh', async () => {
const anotherVisitorWithManager = createFakeVisitor();
await test.step('visitor with contact manager starts a chat', async () => {
await poLiveChat.openAnyLiveChatAndSendMessage({
liveChatUser: anotherVisitorWithManager,
message: 'I need help after refresh test',
isOffline: false,
});
});
await test.step('refresh both agent pages', async () => {
await poHomeOmnichannel.page.reload();
await poHomeOmnichannel.page.waitForLoadState('networkidle');
await poHomeOmnichannelUser2.page.reload();
await poHomeOmnichannelUser2.page.waitForLoadState('networkidle');
});
await test.step('verify non-manager agent still does not see the inquiry after refresh', async () => {
const nonManagerQueuedChat = poHomeOmnichannel.sidebar.getSidebarItemByName(anotherVisitorWithManager.name);
await expect(nonManagerQueuedChat).toHaveCount(0);
});
await test.step('verify the contact manager still sees the inquiry after refresh', async () => {
const managerQueuedChat = poHomeOmnichannelUser2.sidebar.getSidebarItemByName(anotherVisitorWithManager.name);
await expect(managerQueuedChat).toBeVisible();
});
await test.step('contact manager can take the chat after refresh', async () => {
await poHomeOmnichannelUser2.sidebar.getSidebarItemByName(anotherVisitorWithManager.name).click();
await expect(poHomeOmnichannelUser2.content.btnTakeChat).toBeVisible();
await poHomeOmnichannelUser2.content.btnTakeChat.click();
await expect(poHomeOmnichannelUser2.content.lastSystemMessageBody).toHaveText('joined the channel');
});
});
});

View File

@@ -0,0 +1,88 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
const visitor = createFakeVisitor();
test.use({ storageState: Users.user1.state });
test.describe('OC - Livechat - Read Receipts', () => {
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannel: HomeOmnichannel;
let livechatPage: Page;
let agentPage: Page;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.skip(!IS_EE, 'Enterprise Only');
test.beforeAll(async ({ api, browser }) => {
agent = await createAgent(api, 'user1');
await Promise.all([
setSettingValueById(api, 'Message_Read_Receipt_Enabled', true),
setSettingValueById(api, 'Message_Read_Receipt_Store_Users', true),
setSettingValueById(api, 'UI_Use_Real_Name', true),
]);
({ page: agentPage } = await createAuxContext(browser, Users.user1, '/', true));
poHomeOmnichannel = new HomeOmnichannel(agentPage);
({ page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false));
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
});
test.afterAll(({ api }) =>
Promise.all([
setSettingValueById(api, 'Message_Read_Receipt_Enabled', false),
setSettingValueById(api, 'Message_Read_Receipt_Store_Users', false),
setSettingValueById(api, 'UI_Use_Real_Name', false),
agent.delete(),
livechatPage.close(),
agentPage.close(),
]),
);
test('OC - Livechat - Read receipts should display visitor name', async () => {
const testMessage = 'test_message_for_read_receipts';
await test.step('send message from livechat widget', async () => {
await poLiveChat.page.reload();
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(visitor, false);
await poLiveChat.onlineAgentMessage.fill(testMessage);
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage(testMessage)).toBeVisible();
});
await test.step('agent reads the message', async () => {
const markedAsRead = agentPage.waitForResponse(
(res) => /api\/v1\/subscriptions\.read(?:\/|\?|$)/.test(res.url()) && res.request().method() === 'POST',
);
await poHomeOmnichannel.navbar.openChat(visitor.name);
await expect(poHomeOmnichannel.content.lastUserMessage).toBeVisible();
await expect(poHomeOmnichannel.content.lastUserMessage).toContainText(testMessage);
await markedAsRead;
await expect(poHomeOmnichannel.content.lastUserMessage.getByRole('status', { name: 'Message viewed' })).toBeVisible();
});
await test.step('read receipts show both agent and visitor names', async () => {
await poHomeOmnichannel.content.openLastMessageMenu();
await agentPage.locator('role=menuitem[name="Read receipts"]').click();
const dialog = agentPage.getByRole('dialog', { name: 'Read by' });
const listItems = dialog.getByRole('listitem');
await expect(listItems).toHaveCount(2);
await expect(listItems.filter({ hasText: visitor.name })).toHaveCount(1);
await expect(listItems.filter({ hasText: 'user1' })).toHaveCount(1);
await dialog.getByLabel('Close').click();
});
});
});

View File

@@ -0,0 +1,99 @@
import type { BrowserContext } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { createAgent } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
test.describe('OC - Livechat - Cross Tab Communication', () => {
let pageLivechat1: OmnichannelLiveChat;
let pageLivechat2: OmnichannelLiveChat;
let livechatContext: BrowserContext;
let poHomeOmnichannel: HomeOmnichannel;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ browser, api }) => {
agent = await createAgent(api, 'user1');
const { page } = await createAuxContext(browser, Users.user1, '/');
poHomeOmnichannel = new HomeOmnichannel(page);
});
test.beforeEach(async ({ browser, api }) => {
livechatContext = await browser.newContext();
const p1 = await livechatContext.newPage();
const p2 = await livechatContext.newPage();
pageLivechat1 = new OmnichannelLiveChat(p1, api);
pageLivechat2 = new OmnichannelLiveChat(p2, api);
await pageLivechat1.goto();
await pageLivechat2.goto();
});
test.afterEach(async () => {
// Both pages share the same context; closing it disposes both pages
await livechatContext.close();
});
test.afterAll(async () => {
await poHomeOmnichannel.page.close();
await agent.delete();
});
test('OC - Livechat - Send messages, close chat and start again 2 tabs', async () => {
const visitor = createFakeVisitor();
await test.step('expect livechat conversations to be synced', async () => {
await pageLivechat1.openAnyLiveChat();
await pageLivechat1.sendMessage(visitor, false);
await pageLivechat1.onlineAgentMessage.fill('this_a_test_message_from_user');
await pageLivechat1.btnSendMessageToOnlineAgent.click();
await expect(pageLivechat1.txtChatMessage('this_a_test_message_from_user')).toBeVisible();
await expect(pageLivechat2.txtChatMessage('this_a_test_message_from_user')).toBeVisible();
});
await test.step('expect to close livechat conversation', async () => {
await expect(pageLivechat1.btnOptions).toBeVisible();
await pageLivechat1.btnOptions.click();
await expect(pageLivechat1.btnCloseChat).toBeVisible();
await pageLivechat1.btnCloseChat.click();
await pageLivechat1.btnCloseChatConfirm.click();
});
await test.step('expect to restart a livechat conversation and tabs to be synced', async () => {
await expect(pageLivechat1.btnNewChat).toBeVisible();
await pageLivechat1.startNewChat();
await pageLivechat1.onlineAgentMessage.fill('this_a_test_message_from_user_after_close');
await pageLivechat1.btnSendMessageToOnlineAgent.click();
await pageLivechat1.txtChatMessage('this_a_test_message_from_user').waitFor({ state: 'hidden' });
await pageLivechat2.txtChatMessage('this_a_test_message_from_user').waitFor({ state: 'hidden' });
await expect(pageLivechat1.txtChatMessage('this_a_test_message_from_user')).not.toBeVisible();
await expect(pageLivechat2.txtChatMessage('this_a_test_message_from_user')).not.toBeVisible();
await expect(pageLivechat1.txtChatMessage('this_a_test_message_from_user_after_close')).toBeVisible();
await expect(pageLivechat2.txtChatMessage('this_a_test_message_from_user_after_close')).toBeVisible();
});
await test.step('expect to close livechat conversation', async () => {
await expect(pageLivechat1.btnOptions).toBeVisible();
await pageLivechat1.btnOptions.click();
await expect(pageLivechat1.btnCloseChat).toBeVisible();
await pageLivechat1.btnCloseChat.click();
await pageLivechat1.btnCloseChatConfirm.click();
});
});
});

View File

@@ -0,0 +1,60 @@
import type { Page } from 'playwright-core';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
const visitor = createFakeVisitor();
test.use({ storageState: Users.user1.state });
test.describe.serial('OC - Livechat - Typing indicator', () => {
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannel: HomeOmnichannel;
let livechatPage: Page;
test.beforeAll(async ({ api }) => {
const statusCode = (await api.post('/livechat/users/agent', { username: 'user1' })).status();
expect(statusCode).toBe(200);
});
test.beforeAll(async ({ browser, api }) => {
({ page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false));
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
});
test.beforeEach(async ({ page }) => {
poHomeOmnichannel = new HomeOmnichannel(page);
await poHomeOmnichannel.goto();
});
test.afterAll(async ({ api }) => {
await Promise.all([api.delete('/livechat/users/agent/user1'), livechatPage.close()]);
});
test('OC - Livechat - Visitor/Agent types and sees typing indicator', async () => {
await test.step('expect conversation to be started', async () => {
await poLiveChat.page.reload();
await poLiveChat.openAnyLiveChatAndSendMessage({
liveChatUser: visitor,
message: 'Hello, I need help',
isOffline: false,
});
await poHomeOmnichannel.navbar.openChat(visitor.name);
await expect(poHomeOmnichannel.content.lastUserMessage).toBeVisible();
});
await test.step('expect agent to see typing indicator when visitor types', async () => {
await poLiveChat.onlineAgentMessage.pressSequentially('abc', { delay: 20 });
await expect(poHomeOmnichannel.composer.typingIndicator).toBeVisible();
});
await test.step('expect visitor to see typing indicator when agent types', async () => {
await poHomeOmnichannel.composer.inputMessage.pressSequentially('xyz', { delay: 20 });
await expect(poLiveChat.typingIndicatorForVisitor).toBeVisible();
});
});
});

View File

@@ -0,0 +1,73 @@
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { OmnichannelLiveChat, OmnichannelSettings } from '../page-objects/omnichannel';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
const visitor = createFakeVisitor();
test.skip(!IS_EE, 'Enterprise Only');
test.use({ storageState: Users.admin.state });
test.describe('OC - Livechat - Hide watermark', async () => {
let agent: Awaited<ReturnType<typeof createAgent>>;
let poLiveChat: OmnichannelLiveChat;
let poOmnichannelSettings: OmnichannelSettings;
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
const res = await makeAgentAvailable(api, agent.data._id);
await expect(res.status()).toBe(200);
});
test.beforeEach(async ({ browser, api }) => {
const { page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false);
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
});
test.afterEach(async () => {
await poLiveChat.page.close();
});
test.beforeEach(async ({ page }) => {
poOmnichannelSettings = new OmnichannelSettings(page);
await poOmnichannelSettings.goto();
});
test.afterAll(async ({ api }) => {
const res = await api.post('/settings/Livechat_hide_watermark', { value: false });
await expect(res.status()).toBe(200);
});
test('OC - Livechat - Hide watermark', async () => {
await test.step('expect to open Livechat', async () => {
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(visitor, false);
});
await test.step('expect watermark to start visible (default)', async () => {
await expect(poLiveChat.onlineAgentMessage).toBeVisible();
await expect(poLiveChat.txtWatermark).toBeVisible();
});
await test.step('expect to change setting', async () => {
await poOmnichannelSettings.group('Livechat').click();
await poOmnichannelSettings.labelHideWatermark.click();
await poOmnichannelSettings.btnSaveChanges.click();
});
await test.step('expect watermark to be hidden', async () => {
await poLiveChat.page.reload();
await poLiveChat.openLiveChat();
await expect(poLiveChat.onlineAgentMessage).toBeVisible();
await expect(poLiveChat.txtWatermark).toBeHidden();
});
});
});

View File

@@ -0,0 +1,28 @@
import type { Page } from '@playwright/test';
import { OmnichannelLiveChatEmbedded } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe('Omnichannel - Livechat Widget Embedded', () => {
test.describe('Widget is working on Embedded View', () => {
let page: Page;
let poLiveChat: OmnichannelLiveChatEmbedded;
test.beforeAll(async ({ browser }) => {
page = await browser.newPage();
poLiveChat = new OmnichannelLiveChatEmbedded(page);
await poLiveChat.goto();
});
test.afterAll(async () => {
await page.close();
});
test('Open and Close widget', async () => {
await test.step('Expect widget to be visible while embedded in an iframe', async () => {
await expect(poLiveChat.btnOpenLiveChat()).toBeVisible();
});
});
});
});

View File

@@ -0,0 +1,350 @@
import type { Page } from 'playwright-core';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent } from '../utils/omnichannel/agents';
import { test, expect } from '../utils/test';
const firstVisitor = createFakeVisitor();
const secondVisitor = createFakeVisitor();
test.use({ storageState: Users.user1.state });
test.describe.serial('OC - Livechat', () => {
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannel: HomeOmnichannel;
let livechatPage: Page;
test.beforeAll(async ({ api }) => {
const statusCode = (await api.post('/livechat/users/agent', { username: 'user1' })).status();
expect(statusCode).toBe(200);
});
test.beforeAll(async ({ browser, api }) => {
({ page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false));
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
});
test.beforeEach(async ({ page }) => {
poHomeOmnichannel = new HomeOmnichannel(page);
await poHomeOmnichannel.goto();
});
test.afterAll(async ({ api }) => {
await Promise.all([api.delete('/livechat/users/agent/user1'), livechatPage.close()]);
});
test('OC - Livechat - Send message to online agent', async () => {
await test.step('expect message to be sent by livechat', async () => {
await poLiveChat.page.reload();
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(firstVisitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_user"')).toBeVisible();
});
await test.step('expect message to be received by agent', async () => {
await poHomeOmnichannel.navbar.openChat(firstVisitor.name);
await expect(poHomeOmnichannel.content.lastUserMessage).toBeVisible();
await expect(poHomeOmnichannel.content.lastUserMessage).toContainText('this_a_test_message_from_user');
});
});
test('OC - Livechat - Send message to livechat customer', async () => {
await poHomeOmnichannel.navbar.openChat(firstVisitor.name);
await test.step('expect message to be sent by agent', async () => {
await poHomeOmnichannel.content.sendMessage('this_a_test_message_from_agent');
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_agent"')).toBeVisible();
});
await test.step('expect when user minimizes the livechat screen, the composer should be hidden', async () => {
await poLiveChat.openAnyLiveChat();
await expect(poLiveChat.page.locator('[contenteditable="true"]')).not.toBeVisible();
});
await test.step('expect message to be received by minimized livechat', async () => {
await poHomeOmnichannel.content.sendMessage('this_a_test_message_again_from_agent');
await expect(poLiveChat.unreadMessagesBadge(1)).toBeVisible();
});
await test.step('expect multiple messages to be received by minimized livechat', async () => {
await poHomeOmnichannel.content.sendMessage('this_a_test_message_once_again_from_agent');
await expect(poLiveChat.unreadMessagesBadge(2)).toBeVisible();
});
await test.step('expect unread messages to be visible after a reload', async () => {
await poLiveChat.page.reload();
await expect(poLiveChat.unreadMessagesBadge(2)).toBeVisible();
});
await test.step('expect unread counter to be empty after user sends a message after reload', async () => {
const message = 'this_a_test_message_from_user_after_reload';
await poLiveChat.openAnyLiveChat();
await poLiveChat.onlineAgentMessage.fill(message);
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage(message)).toBeVisible();
await expect(poLiveChat.unreadMessagesBadge(2)).toHaveCount(0);
await expect(poLiveChat.unreadMessagesBadge(1)).toHaveCount(0);
});
});
test('OC - Livechat - Close livechat conversation', async () => {
await poHomeOmnichannel.navbar.openChat(firstVisitor.name);
await test.step('expect livechat conversation to be closed by agent', async () => {
await poHomeOmnichannel.quickActionsRoomToolbar.closeChat({ comment: 'this_is_a_test_comment' });
});
});
});
test.describe.serial('OC - Livechat - Visitors closing the room is disabled', () => {
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannel: HomeOmnichannel;
let livechatPage: Page;
let omniPage: Page;
test.beforeAll(async ({ api }) => {
await api.post('/livechat/users/agent', { username: 'user1' });
});
test.beforeAll(async ({ browser, api }) => {
({ page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false));
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
});
test.beforeAll(async ({ browser, api }) => {
await setSettingValueById(api, 'Livechat_allow_visitor_closing_chat', false);
({ page: omniPage } = await createAuxContext(browser, Users.user1, '/', true));
poHomeOmnichannel = new HomeOmnichannel(omniPage);
});
test.afterAll(async ({ api }) => {
await Promise.all([
setSettingValueById(api, 'Livechat_allow_visitor_closing_chat', true),
api.delete('/livechat/users/agent/user1'),
livechatPage.close(),
omniPage.close(),
]);
});
test('OC - Livechat - Close Chat disabled', async () => {
await poLiveChat.page.reload();
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(firstVisitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await test.step('expect to close a livechat conversation', async () => {
await expect(poLiveChat.btnOptions).not.toBeVisible();
await expect(poLiveChat.btnCloseChat).not.toBeVisible();
});
});
test('OC - Livechat - Close chat disabled, agents can close', async () => {
await poHomeOmnichannel.navbar.openChat(firstVisitor.name);
await test.step('expect livechat conversation to be closed by agent', async () => {
await poHomeOmnichannel.quickActionsRoomToolbar.closeChat({ comment: 'this_is_a_test_comment' });
});
});
});
test.describe.serial('OC - Livechat - Resub after close room', () => {
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannel: HomeOmnichannel;
let livechatPage: Page;
let omniPage: Page;
test.beforeAll(async ({ api }) => {
const statusCode = (await api.post('/livechat/users/agent', { username: 'user1' })).status();
expect(statusCode).toBe(200);
});
test.beforeAll(async ({ browser, api }) => {
await api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: true });
({ page: omniPage } = await createAuxContext(browser, Users.user1, '/', true));
poHomeOmnichannel = new HomeOmnichannel(omniPage);
({ page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false));
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
await poLiveChat.sendMessageAndCloseChat(firstVisitor);
});
test.afterAll(async ({ api }) => {
await Promise.all([
api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: false }),
api.delete('/livechat/users/agent/user1'),
livechatPage.close(),
omniPage.close(),
]);
});
test('OC - Livechat - Resub after close room', async () => {
await test.step('expect livechat conversation to be opened again, different guest', async () => {
await poLiveChat.startNewChat();
await poLiveChat.sendMessage(secondVisitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_user"')).toBeVisible();
});
await test.step('expect message to be received by agent', async () => {
await poHomeOmnichannel.navbar.openChat(secondVisitor.name);
await expect(poHomeOmnichannel.content.lastUserMessage).toBeVisible();
await expect(poHomeOmnichannel.content.lastUserMessage).toContainText('this_a_test_message_from_user');
});
await test.step('expect message to be sent by agent', async () => {
await poHomeOmnichannel.content.sendMessage('this_a_test_message_from_agent');
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_agent"')).toBeVisible();
});
});
});
test.describe('OC - Livechat - Resume chat after closing', () => {
let poLiveChat: OmnichannelLiveChat;
let poHomeOmnichannel: HomeOmnichannel;
let livechatPage: Page;
let omniPage: Page;
test.beforeAll(async ({ api }) => {
const statusCode = (await api.post('/livechat/users/agent', { username: 'user1' })).status();
expect(statusCode).toBe(200);
});
test.beforeAll(async ({ browser, api }) => {
({ page: omniPage } = await createAuxContext(browser, Users.user1, '/', true));
poHomeOmnichannel = new HomeOmnichannel(omniPage);
({ page: livechatPage } = await createAuxContext(browser, Users.user1, '/livechat', false));
poLiveChat = new OmnichannelLiveChat(livechatPage, api);
await poLiveChat.sendMessageAndCloseChat(firstVisitor);
});
test.afterAll(async ({ api }) => {
await Promise.all([api.delete('/livechat/users/agent/user1'), livechatPage.close(), omniPage.close()]);
});
test('OC - Livechat - Resume chat after closing', async () => {
await test.step('expect livechat conversation to be opened again', async () => {
await poLiveChat.startNewChat();
await expect(poLiveChat.onlineAgentMessage).toBeVisible();
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_user"')).toBeVisible();
});
await test.step('expect message to be received by agent', async () => {
await poHomeOmnichannel.navbar.openChat(firstVisitor.name);
await expect(poHomeOmnichannel.content.lastUserMessage).toBeVisible();
await expect(poHomeOmnichannel.content.lastUserMessage).toContainText('this_a_test_message_from_user');
});
await test.step('expect message to be sent by agent', async () => {
await poHomeOmnichannel.content.sendMessage('this_a_test_message_from_agent');
await expect(poLiveChat.page.locator('div >> text="this_a_test_message_from_agent"')).toBeVisible();
});
});
});
test.describe('OC - Livechat - Close chat using widget', () => {
let poLiveChat: OmnichannelLiveChat;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ api, browser }) => {
agent = await createAgent(api, 'user1');
await createAuxContext(browser, Users.user1, '/', true);
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
});
test.afterAll(async () => {
await agent.delete();
});
test('OC - Livechat - Close Chat', async () => {
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(firstVisitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await test.step('expect to close a livechat conversation', async () => {
await expect(poLiveChat.btnOptions).toBeVisible();
await poLiveChat.btnOptions.click();
await expect(poLiveChat.btnCloseChat).toBeVisible();
await poLiveChat.btnCloseChat.click();
await poLiveChat.btnCloseChatConfirm.click();
await expect(poLiveChat.btnNewChat).toBeVisible();
});
});
test('OC - Livechat - Close Chat twice', async () => {
await poLiveChat.sendMessageAndCloseChat(firstVisitor);
await poLiveChat.startNewChat();
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await test.step('expect to close a livechat conversation a second time', async () => {
await expect(poLiveChat.btnOptions).toBeVisible();
await poLiveChat.btnOptions.click();
await expect(poLiveChat.btnCloseChat).toBeVisible();
await poLiveChat.btnCloseChat.click();
await poLiveChat.btnCloseChatConfirm.click();
await expect(poLiveChat.btnNewChat).toBeVisible();
});
});
});
test.describe('OC - Livechat - Livechat_Display_Offline_Form', () => {
let poLiveChat: OmnichannelLiveChat;
const message = 'This form is not available';
test.beforeAll(async ({ api }) => {
await api.post('/settings/Livechat_display_offline_form', { value: false });
await api.post('/settings/Livechat_offline_form_unavailable', { value: message });
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
});
test.afterAll(async ({ api }) => {
await api.post('/settings/Livechat_display_offline_form', { value: true });
await api.post('/settings/Livechat_offline_form_unavailable', { value: '' });
});
test('OC - Livechat - Livechat_Display_Offline_Form false', async () => {
await test.step('expect offline form to not be visible', async () => {
await poLiveChat.openAnyLiveChat();
await expect(poLiveChat.page.locator(`div >> text=${message}`)).toBeVisible();
await expect(poLiveChat.textAreaMessage).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,229 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelAgents, OmnichannelManager, OmnichannelMonitors } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { createDepartment } from '../utils/omnichannel/departments';
import { createManager } from '../utils/omnichannel/managers';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
const MANAGER = 'user3';
const ROOM_A = faker.person.fullName();
const ROOM_B = faker.person.fullName();
const ROOM_C = faker.person.fullName();
test.use({ storageState: Users.user3.state });
test.describe('OC - Manager Role', () => {
test.skip(!IS_EE, 'Enterprise Edition Only');
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let conversations: Awaited<ReturnType<typeof createConversation>>[];
let agents: Awaited<ReturnType<typeof createAgent>>[];
let poOmnichannel: HomeOmnichannel;
let manager: Awaited<ReturnType<typeof createManager>>;
// Allow manual on hold
test.beforeAll(async ({ api }) => {
const responses = await Promise.all([
api.post('/settings/Livechat_allow_manual_on_hold', { value: true }),
api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: false }),
]);
responses.forEach((res) => expect(res.status()).toBe(200));
});
// Create agents
test.beforeAll(async ({ api }) => {
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2'), createAgent(api, MANAGER)]);
const agentsStatuses = await Promise.all(agents.slice(0, 2).map(({ data: agent }) => makeAgentAvailable(api, agent._id)));
agentsStatuses.forEach((res) => expect(res.status()).toBe(200));
});
// Create departments
test.beforeAll(async ({ api }) => {
departments = await Promise.all([createDepartment(api), createDepartment(api)]);
});
// Create manager
test.beforeAll(async ({ api }) => {
manager = await createManager(api, MANAGER);
});
// Create conversations
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map(({ data }) => data);
conversations = await Promise.all([
createConversation(api, {
visitorName: ROOM_A,
visitorToken: 'roomA',
agentId: `user1`,
departmentId: departmentA._id,
}),
createConversation(api, {
visitorName: ROOM_B,
visitorToken: 'roomB',
agentId: `user2`,
departmentId: departmentB._id,
}),
createConversation(api, {
visitorName: ROOM_C,
visitorToken: 'roomC',
agentId: `user2`,
}),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', true);
});
// Delete all created data
test.afterAll(async ({ api }) => {
await Promise.all([
...agents.map((agent) => agent.delete()),
...departments.map((department) => department.delete()),
...conversations.map((conversation) => conversation.delete()),
manager.delete(),
// Reset setting
api.post('/settings/Livechat_allow_manual_on_hold', { value: false }),
api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: true }),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', false);
});
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannel = new HomeOmnichannel(page);
await poOmnichannel.chats.goto();
});
test('OC - Manager Role - Basic permissions', async () => {
await test.step('expect agent to not have access to omnichannel administration', async () => {
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Contact Center')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Analytics')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Real-time Monitoring')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Agents')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Departments')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Business Hours')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Reports')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Canned Responses')).toBeVisible();
});
});
test('OC - Manager Role - Contact Center', async () => {
await test.step('expect to be able to view all chats', async () => {
await expect(poOmnichannel.chats.table.findRowByName(ROOM_A)).toBeVisible();
await expect(poOmnichannel.chats.table.findRowByName(ROOM_B)).toBeVisible();
await expect(poOmnichannel.chats.table.findRowByName(ROOM_C)).toBeVisible();
});
await test.step('expect to be able to join chats', async () => {
await poOmnichannel.chats.openChat(ROOM_A);
await expect(poOmnichannel.composer.btnJoinRoom).toBeVisible();
await expect(poOmnichannel.composer.inputMessage).not.toBeVisible();
await poOmnichannel.composer.btnJoinRoom.click();
await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText('joined the channel');
await expect(poOmnichannel.composer.btnJoinRoom).not.toBeVisible();
await expect(poOmnichannel.composer.inputMessage).toBeVisible();
});
await test.step('expect to be able to put a conversation from another agent on hold', async () => {
await poOmnichannel.quickActionsRoomToolbar.placeChatOnHold();
await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText(
`Chat On Hold: The chat was manually placed On Hold by ${MANAGER}`,
);
await expect(poOmnichannel.composer.inputMessage).not.toBeVisible();
await expect(poOmnichannel.content.btnResume).toBeVisible();
});
await test.step('expect to be able resume a conversation from another agent on hold', async () => {
await poOmnichannel.content.btnResume.click();
await expect(poOmnichannel.content.btnResume).not.toBeVisible();
await expect(poOmnichannel.composer.inputMessage).toBeVisible();
await expect(poOmnichannel.quickActionsRoomToolbar.btnOnHold).toBeVisible();
});
await test.step('expect to be able to close a conversation from another agent', async () => {
await poOmnichannel.quickActionsRoomToolbar.closeChat();
await poOmnichannel.chats.goto();
});
await test.step('expect to be able to remove closed rooms', async () => {
await poOmnichannel.chats.removeChatByName(ROOM_A);
await expect(poOmnichannel.chats.table.findRowByName(ROOM_A)).not.toBeVisible();
});
});
test('OC - Manager Role - Add/remove agents', async ({ page }) => {
const poOmnichannelAgents = new OmnichannelAgents(page);
await poOmnichannelAgents.goto();
await test.step('expect add "user1" as agent', async () => {
await poOmnichannelAgents.selectUsername('user1');
await poOmnichannelAgents.btnAddAgent.click();
await poOmnichannelAgents.inputSearch.fill('user1');
await expect(poOmnichannelAgents.table.findRowByName('user1')).toBeVisible();
});
await test.step('expect remove "user1" as agent', async () => {
await poOmnichannelAgents.inputSearch.fill('user1');
await poOmnichannelAgents.deleteAgent('user1');
await poOmnichannelAgents.inputSearch.fill('');
await poOmnichannelAgents.inputSearch.fill('user1');
await expect(poOmnichannelAgents.table.findRowByName('user1')).toBeHidden();
});
});
test('OC - Manager Role - Add/remove managers', async ({ page }) => {
const poOmnichannelManagers = new OmnichannelManager(page);
await poOmnichannelManagers.goto();
await test.step('expect add "user1" as manager', async () => {
await poOmnichannelManagers.selectUsername('user1');
await poOmnichannelManagers.btnAddManager.click();
await expect(poOmnichannelManagers.table.findRowByName('user1')).toBeVisible();
});
await test.step('expect search for manager', async () => {
await poOmnichannelManagers.search('user1');
await expect(poOmnichannelManagers.table.findRowByName('user1')).toBeVisible();
await poOmnichannelManagers.search('NonExistingUser');
await expect(poOmnichannelManagers.table.findRowByName('user1')).toBeHidden();
await poOmnichannelManagers.clearSearch();
});
await test.step('expect remove "user1" as manager', async () => {
await poOmnichannelManagers.search('user1');
await poOmnichannelManagers.removeManager('user1');
await expect(poOmnichannelManagers.table.findRowByName('user1')).toBeHidden();
});
});
test('OC - Manager Role - Add/remove monitors', async ({ page }) => {
const poOmnichannelMonitors = new OmnichannelMonitors(page);
await poOmnichannelMonitors.goto();
await test.step('expect to add agent as monitor', async () => {
await expect(poOmnichannelMonitors.table.findRowByName('user1')).not.toBeVisible();
await poOmnichannelMonitors.addMonitor('user1');
await expect(poOmnichannelMonitors.table.findRowByName('user1')).toBeVisible();
});
await test.step('expect to remove agent from monitor', async () => {
await poOmnichannelMonitors.removeMonitor('user1');
await expect(poOmnichannelMonitors.table.findRowByName('user1')).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,45 @@
import { Users } from '../fixtures/userStates';
import { OmnichannelManager } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe.serial('omnichannel-manager', () => {
let poOmnichannelManagers: OmnichannelManager;
test.beforeEach(async ({ page }) => {
poOmnichannelManagers = new OmnichannelManager(page);
await poOmnichannelManagers.goto();
});
test('OC - Manage Managers - Add, Search and Remove', async ({ page }) => {
await test.step('expect "user1" be first ', async () => {
await poOmnichannelManagers.inputUsername.fill('user');
await expect(page.locator('role=option[name="user1"]')).toContainText('user1');
});
await test.step('expect add "user1" as manager', async () => {
await poOmnichannelManagers.selectUsername('user1');
await poOmnichannelManagers.btnAddManager.click();
await expect(poOmnichannelManagers.table.findRowByName('user1')).toBeVisible();
});
await test.step('expect search for manager', async () => {
await poOmnichannelManagers.search('user1');
await expect(poOmnichannelManagers.table.findRowByName('user1')).toBeVisible();
await poOmnichannelManagers.search('NonExistingUser');
await expect(poOmnichannelManagers.table.findRowByName('user1')).toBeHidden();
await poOmnichannelManagers.clearSearch();
});
await test.step('expect remove "user1" as manager', async () => {
await poOmnichannelManagers.search('user1');
await poOmnichannelManagers.removeManager('user1');
await expect(poOmnichannelManagers.table.findRowByName('user1')).toBeHidden();
});
});
});

View File

@@ -0,0 +1,76 @@
import type { Page } from '@playwright/test';
import { DEFAULT_USER_CREDENTIALS } from '../config/constants';
import injectInitialData from '../fixtures/inject-initial-data';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.user1.state });
test.describe('OC - Manual Selection After Relogin', () => {
let poOmnichannel: HomeOmnichannel;
let agent: Awaited<ReturnType<typeof createAgent>>;
// Change routing method to manual selection
test.beforeAll(async ({ api }) => {
const res = await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' });
expect(res.status()).toBe(200);
});
// Create agent and make it available
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user1');
await makeAgentAvailable(api, agent.data._id);
});
// Create page object and redirect to home
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannel = new HomeOmnichannel(page);
await poOmnichannel.goto();
await poOmnichannel.navbar.logout();
await poOmnichannel.page.locator('role=textbox[name=/username/i]').waitFor({ state: 'visible' });
await poOmnichannel.page.locator('role=textbox[name=/username/i]').fill('user1');
await poOmnichannel.page.locator('[name=password]').fill(DEFAULT_USER_CREDENTIALS.password);
await poOmnichannel.page.locator('role=button[name="Login"]').click();
await poOmnichannel.page.locator('#main-content').waitFor();
});
// Delete all data
test.afterAll(async ({ api }) => {
await agent.delete();
await api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' });
await injectInitialData();
});
test('OC - Manual Selection - Logout & Login', async ({ api }) => {
expect(await poOmnichannel.page.locator('#omnichannel-status-toggle').getAttribute('title')).toEqual('Turn off answer chats');
const {
data: { room },
} = await createConversation(api);
await test.step('expect login and see the chat in queue after login', async () => {
await poOmnichannel.sidebar.getSidebarItemByName(room.fname).click();
await expect(poOmnichannel.composer.inputMessage).not.toBeVisible();
});
await test.step('expect take chat to be visible and return to queue not visible', async () => {
await expect(poOmnichannel.content.btnTakeChat).toBeVisible();
await expect(poOmnichannel.content.btnReturnToQueue).not.toBeVisible();
});
await test.step('expect to be able take chat', async () => {
await poOmnichannel.content.btnTakeChat.click();
await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText('joined the channel');
await expect(poOmnichannel.composer.inputMessage).toBeVisible();
await expect(poOmnichannel.content.btnTakeChat).not.toBeVisible();
await expect(poOmnichannel.content.btnReturnToQueue).toBeVisible();
await expect(poOmnichannel.sidebar.getSidebarItemByName(room.fname)).toBeVisible();
});
});
});

View File

@@ -0,0 +1,105 @@
import type { Page } from '@playwright/test';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.user3.state });
test.describe('OC - Manual Selection', () => {
let poOmnichannel: HomeOmnichannel;
let agents: Awaited<ReturnType<typeof createAgent>>[];
let agentB: { page: Page; poHomeOmnichannel: HomeOmnichannel };
// Change routing method to manual selection
test.beforeAll(async ({ api }) => {
const res = await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' });
expect(res.status()).toBe(200);
});
// Create agent and make it available
test.beforeAll(async ({ api }) => {
agents = await Promise.all([await createAgent(api, 'user3'), await createAgent(api, 'user1')]);
(await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)))).forEach((res) => {
expect(res.status()).toBe(200);
});
});
// Create page object and redirect to home
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannel = new HomeOmnichannel(page);
await poOmnichannel.goto();
});
// Create agent b session
test.beforeEach(async ({ browser }) => {
agentB = await createAuxContext(browser, Users.user1).then(({ page }) => ({ page, poHomeOmnichannel: new HomeOmnichannel(page) }));
});
test.afterEach(async () => {
await agentB.page.close();
});
// Delete all data
test.afterAll(async ({ api }) => {
await Promise.all([
...agents.map((agent) => agent.delete()),
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }),
]);
});
test('OC - Manual Selection - Queue', async ({ page, api }) => {
const {
data: { room },
} = await createConversation(api);
await test.step('expect not be able to see queue when livechat is disabled', async () => {
await poOmnichannel.navbar.switchOmnichannelStatus('offline');
await agentB.poHomeOmnichannel.navbar.switchOmnichannelStatus('offline');
await expect(poOmnichannel.sidebar.getSidebarItemByName(room.fname)).not.toBeVisible();
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(room.fname)).not.toBeVisible();
await poOmnichannel.navbar.switchOmnichannelStatus('online');
await agentB.poHomeOmnichannel.navbar.switchOmnichannelStatus('online');
await expect(poOmnichannel.sidebar.getSidebarItemByName(room.fname)).toBeVisible();
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(room.fname)).toBeVisible();
});
await test.step('expect to be able join chat in read mode', async () => {
await poOmnichannel.sidebar.getSidebarItemByName(room.fname).click();
await expect(poOmnichannel.composer.inputMessage).not.toBeVisible();
await expect(poOmnichannel.content.btnTakeChat).toBeVisible();
});
await test.step('expect to be able take chat', async () => {
await poOmnichannel.content.btnTakeChat.click();
await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText('joined the channel');
await expect(poOmnichannel.composer.inputMessage).toBeVisible();
await expect(poOmnichannel.content.btnTakeChat).not.toBeVisible();
await expect(poOmnichannel.sidebar.getSidebarItemByName(room.fname)).toBeVisible();
});
await test.step('expect chat to leave the queue', async () => {
await page.waitForTimeout(250);
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(room.fname)).not.toBeVisible();
});
await test.step('expect to be able return to queue', async () => {
await poOmnichannel.content.btnReturnToQueue.click();
await poOmnichannel.content.returnToQueueModal.confirm();
await expect(page).toHaveURL('/home');
});
await test.step('expect chat to be back in queue', async () => {
// the returning agent can transiently see the room twice (queued inquiry + not-yet-removed subscription)
await expect(poOmnichannel.sidebar.getSidebarItemByName(room.fname)).toHaveCount(1);
await expect(poOmnichannel.sidebar.getSidebarItemByName(room.fname)).toBeVisible();
await expect(agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(room.fname)).toBeVisible();
await poOmnichannel.sidebar.getSidebarItemByName(room.fname).click();
await expect(poOmnichannel.composer.inputMessage).not.toBeVisible();
await expect(poOmnichannel.content.btnTakeChat).toBeVisible();
});
});
});

View File

@@ -0,0 +1,184 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelDepartments } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent } from '../utils/omnichannel/agents';
import { createDepartment } from '../utils/omnichannel/departments';
import { createMonitor } from '../utils/omnichannel/monitors';
import { createOrUpdateUnit } from '../utils/omnichannel/units';
import { test, expect } from '../utils/test';
const MONITOR = 'user3';
test.use({ storageState: Users.user3.state });
test.describe.serial('OC - Monitor Role', () => {
test.skip(!IS_EE, 'Enterprise Edition Only');
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let agents: Awaited<ReturnType<typeof createAgent>>[];
let monitor: Awaited<ReturnType<typeof createMonitor>>;
let units: Awaited<ReturnType<typeof createOrUpdateUnit>>[];
let poOmnichannelDepartments: OmnichannelDepartments;
const newDepartmentName = faker.string.uuid();
// Reset user3 roles
test.beforeAll(async ({ api }) => {
const res = await api.post('/users.update', { data: { roles: ['user'] }, userId: MONITOR });
await expect(res.status()).toBe(200);
});
// Create agents
test.beforeAll(async ({ api }) => {
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]);
});
// Create department
test.beforeAll(async ({ api }) => {
departments = await Promise.all([createDepartment(api), createDepartment(api)]);
});
// Create monitor
test.beforeAll(async ({ api }) => {
monitor = await createMonitor(api, MONITOR);
});
// Create unit
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB] = departments.map((dep) => dep.data);
units = await Promise.all([
await createOrUpdateUnit(api, {
monitors: [{ monitorId: 'user2', username: 'user2' }],
departments: [{ departmentId: departmentB._id }],
}),
await createOrUpdateUnit(api, {
monitors: [{ monitorId: MONITOR, username: MONITOR }],
departments: [{ departmentId: departmentA._id }],
}),
await createOrUpdateUnit(api, {
monitors: [{ monitorId: MONITOR, username: MONITOR }],
departments: [{ departmentId: departmentA._id }],
}),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', true);
});
// Delete all created data
test.afterAll(async ({ api }) => {
await Promise.all([
...agents.map((agent) => agent.delete()),
...departments.map((department) => department.delete()),
...units.map((unit) => unit.delete()),
monitor.delete(),
]);
await setSettingValueById(api, 'Omnichannel_enable_department_removal', false);
});
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannelDepartments = new OmnichannelDepartments(page);
await poOmnichannelDepartments.goto();
});
test('OC - Monitor Role - Create department with business unit', async () => {
const [departmentA] = departments.map((dep) => dep.data);
const [unitA, unitB, unitC] = units.map((unit) => unit.data);
await test.step('expect to see only departmentA in the list', async () => {
await expect(poOmnichannelDepartments.table.findRowByName(departmentA.name)).toBeVisible();
});
await test.step('expect to fill departments mandatory field', async () => {
await poOmnichannelDepartments.createNew();
await poOmnichannelDepartments.inputName.fill(newDepartmentName);
await poOmnichannelDepartments.inputEmail.fill(faker.internet.email());
});
await test.step('expect to only have the units from monitor visible', async () => {
await expect(poOmnichannelDepartments.inputUnit).not.toBeDisabled();
await poOmnichannelDepartments.inputUnit.click();
await expect(poOmnichannelDepartments.findOption(unitA.name)).not.toBeVisible();
await expect(poOmnichannelDepartments.findOption(unitB.name)).toBeVisible();
await expect(poOmnichannelDepartments.findOption(unitC.name)).toBeVisible();
});
await test.step('expect to be able to switch freely between available units', async () => {
await poOmnichannelDepartments.findOption(unitC.name).click();
await expect(poOmnichannelDepartments.inputUnit).not.toBeDisabled();
await poOmnichannelDepartments.inputUnit.click();
await poOmnichannelDepartments.findOption(unitB.name).click();
});
await test.step('expect unit field to be required', async () => {
await poOmnichannelDepartments.inputUnit.click();
await poOmnichannelDepartments.findOption('None').click();
await poOmnichannelDepartments.btnSave.click();
await expect(poOmnichannelDepartments.errorMessage('Unit required')).toBeVisible();
await poOmnichannelDepartments.inputUnit.click();
await poOmnichannelDepartments.findOption(unitB.name).click();
await expect(poOmnichannelDepartments.errorMessage('Unit required')).not.toBeVisible();
});
await test.step('expect to save department', async () => {
await poOmnichannelDepartments.labelEnabled.click();
await poOmnichannelDepartments.btnSave.click();
});
await test.step('expect to have departmentA and departmentB visible', async () => {
await expect(poOmnichannelDepartments.table.findRowByName(departmentA.name)).toBeVisible();
await expect(poOmnichannelDepartments.table.findRowByName(newDepartmentName)).toBeVisible();
});
});
test('OC - Monitor Role - Not allow editing department business unit', async () => {
await test.step('expect not to be able to edit unit', async () => {
await poOmnichannelDepartments.search(newDepartmentName);
await poOmnichannelDepartments.getDepartmentMenuByName(newDepartmentName).click();
await poOmnichannelDepartments.menuEditOption.click();
await expect(poOmnichannelDepartments.inputUnit).toBeDisabled();
});
});
// TODO: We are going to prevent editing busines unit for now
test.skip('OC - Monitor Role - Edit department business unit', async () => {
const [departmentA] = departments.map((dep) => dep.data);
const [, , unitC] = units.map((unit) => unit.data);
await test.step('expect to edit unit', async () => {
await poOmnichannelDepartments.search(newDepartmentName);
await poOmnichannelDepartments.getDepartmentMenuByName(newDepartmentName).click();
await poOmnichannelDepartments.menuEditOption.click();
await poOmnichannelDepartments.selectUnit(unitC.name);
await poOmnichannelDepartments.labelEnabled.click();
await poOmnichannelDepartments.btnSave.click();
});
await test.step('expect departmentB to still be visible', async () => {
await expect(poOmnichannelDepartments.table.findRowByName(departmentA.name)).toBeVisible();
await expect(poOmnichannelDepartments.table.findRowByName(newDepartmentName)).toBeVisible();
});
});
test.skip('OC - Monitor Role - Edit department and remove business unit', async () => {
const [departmentA] = departments.map((dep) => dep.data);
await test.step('expect to edit unit', async () => {
await poOmnichannelDepartments.search(newDepartmentName);
await poOmnichannelDepartments.getDepartmentMenuByName(newDepartmentName).click();
await poOmnichannelDepartments.menuEditOption.click();
await poOmnichannelDepartments.selectUnit('None');
await poOmnichannelDepartments.labelEnabled.click();
await poOmnichannelDepartments.btnSave.click();
});
await test.step('expect departmentB to not be visible', async () => {
await expect(poOmnichannelDepartments.table.findRowByName(departmentA.name)).toBeVisible();
await expect(poOmnichannelDepartments.table.findRowByName(newDepartmentName)).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,273 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { setSettingValueById } from '../utils';
import { createAgent, makeAgentAvailable } from '../utils/omnichannel/agents';
import { createDepartment } from '../utils/omnichannel/departments';
import { createMonitor } from '../utils/omnichannel/monitors';
import { createConversation } from '../utils/omnichannel/rooms';
import { createOrUpdateUnit, fetchUnitMonitors } from '../utils/omnichannel/units';
import { test, expect } from '../utils/test';
const MONITOR = 'user3';
const MONITOR_ADMIN = 'rocketchat.internal.admin.test';
const ROOM_A = faker.person.fullName();
const ROOM_B = faker.person.fullName();
const ROOM_C = faker.person.fullName();
const ROOM_D = faker.person.fullName();
test.use({ storageState: Users.user3.state });
test.describe('OC - Monitor Role', () => {
test.skip(!IS_EE, 'Enterprise Edition Only');
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let conversations: Awaited<ReturnType<typeof createConversation>>[];
let agents: Awaited<ReturnType<typeof createAgent>>[];
let monitors: Awaited<ReturnType<typeof createMonitor>>[];
let units: Awaited<ReturnType<typeof createOrUpdateUnit>>[];
let poOmnichannel: HomeOmnichannel;
// Reset user3 roles
test.beforeAll(async ({ api }) => {
const res = await api.post('/users.update', {
data: { roles: ['user'] },
userId: 'user3',
});
expect(res.status()).toBe(200);
});
// Allow manual on hold
test.beforeAll(async ({ api }) => {
const responses = await Promise.all([
api.post('/settings/Livechat_allow_manual_on_hold', { value: true }),
api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: false }),
setSettingValueById(api, 'Omnichannel_enable_department_removal', true),
// This is required now we're sending a chat into a department with no agents and no default agent
api.post('/settings/Livechat_accept_chats_with_no_agents', { value: true }),
]);
responses.forEach((res) => expect(res.status()).toBe(200));
});
// Create agents
test.beforeAll(async ({ api }) => {
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]);
const agentsStatuses = await Promise.all(agents.map(({ data: agent }) => makeAgentAvailable(api, agent._id)));
agentsStatuses.forEach((res) => expect(res.status()).toBe(200));
});
// Create departments
test.beforeAll(async ({ api }) => {
departments = await Promise.all([createDepartment(api), createDepartment(api), createDepartment(api)]);
});
// Create conversations
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB, departmentC] = departments.map(({ data }) => data);
conversations = await Promise.all([
createConversation(api, {
visitorName: ROOM_A,
agentId: `user1`,
departmentId: departmentA._id,
}),
createConversation(api, {
visitorName: ROOM_B,
agentId: `user2`,
departmentId: departmentA._id,
}),
createConversation(api, {
visitorName: ROOM_C,
agentId: `user2`,
departmentId: departmentB._id,
}),
createConversation(api, {
visitorName: ROOM_D,
departmentId: departmentC._id,
}),
]);
});
// Create monitors
test.beforeAll(async ({ api }) => {
monitors = await Promise.all([createMonitor(api, MONITOR), createMonitor(api, MONITOR_ADMIN)]);
});
// Create units
test.beforeAll(async ({ api }) => {
const [departmentA, departmentB, departmentC] = departments.map(({ data }) => data);
units = await Promise.all([
createOrUpdateUnit(api, {
monitors: [{ monitorId: MONITOR, username: MONITOR }],
departments: [{ departmentId: departmentA._id }, { departmentId: departmentB._id }],
}),
createOrUpdateUnit(api, {
monitors: [{ monitorId: MONITOR_ADMIN, username: MONITOR_ADMIN }],
departments: [{ departmentId: departmentC._id }],
}),
]);
});
// Delete all created data
test.afterAll(async ({ api }) => {
await Promise.all([
...agents.map((agent) => agent.delete()),
...departments.map((department) => department.delete()),
...conversations.map((conversation) => conversation.delete()),
...units.map((unit) => unit.delete()),
...monitors.map((monitor) => monitor.delete()),
// Reset setting
api.post('/settings/Livechat_allow_manual_on_hold', { value: false }),
api.post('/settings/Livechat_allow_manual_on_hold_upon_agent_engagement_only', { value: true }),
setSettingValueById(api, 'Omnichannel_enable_department_removal', false),
api.post('/settings/Livechat_accept_chats_with_no_agents', { value: false }),
]);
});
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannel = new HomeOmnichannel(page);
await poOmnichannel.chats.goto();
});
test('OC - Monitor Role - Basic permissions', async () => {
await test.step('expect agent to not have access to omnichannel administration', async () => {
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Contact Center')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Analytics')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Real-time Monitoring')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Agents')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Departments')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Business Hours')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Reports')).toBeVisible();
await expect(poOmnichannel.omnisidenav.getSidebarLinkByName('Canned Responses')).toBeVisible();
});
// await test.step('expect to be able to see contact center', async () => {});
// await test.step('expect to be able to see queue', async () => {});
// await test.step('expect to be able to edit custom fields', async () => {});
});
test('OC - Monitor Role - Canned responses', async () => {
// TODO: move to unit test
await test.step('expect not to be able to create public canned responses (administration)', async () => {
await poOmnichannel.cannedResponses.goto();
await poOmnichannel.cannedResponses.btnNew.click();
await expect(poOmnichannel.cannedResponses.radioPublic).toBeDisabled();
});
});
test('OC - Monitor Role - Contact Center', async () => {
await test.step('expect to be able to view only chats from same unit', async () => {
await expect(poOmnichannel.chats.table.findRowByName(ROOM_A)).toBeVisible();
await expect(poOmnichannel.chats.table.findRowByName(ROOM_B)).toBeVisible();
await expect(poOmnichannel.chats.table.findRowByName(ROOM_C)).toBeVisible();
await expect(poOmnichannel.chats.table.findRowByName(ROOM_D)).not.toBeVisible();
});
await test.step('expect to be able to join chats from same unit', async () => {
await poOmnichannel.chats.openChat(ROOM_A);
await expect(poOmnichannel.composer.btnJoinRoom).toBeVisible();
await expect(poOmnichannel.composer.inputMessage).not.toBeVisible();
await poOmnichannel.composer.btnJoinRoom.click();
await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText('joined the channel');
await expect(poOmnichannel.composer.btnJoinRoom).not.toBeVisible();
await expect(poOmnichannel.composer.inputMessage).toBeVisible();
});
await test.step('expect to be able to put a conversation from another agent on hold', async () => {
await poOmnichannel.quickActionsRoomToolbar.placeChatOnHold();
await expect(poOmnichannel.content.lastSystemMessageBody).toHaveText(
`Chat On Hold: The chat was manually placed On Hold by ${MONITOR}`,
);
await expect(poOmnichannel.composer.inputMessage).not.toBeVisible();
await expect(poOmnichannel.content.btnResume).toBeVisible();
});
await test.step('expect to be able resume a conversation from another agent on hold', async () => {
await poOmnichannel.content.btnResume.click();
await expect(poOmnichannel.content.btnResume).not.toBeVisible();
await expect(poOmnichannel.composer.inputMessage).toBeVisible();
await expect(poOmnichannel.quickActionsRoomToolbar.btnOnHold).toBeVisible();
});
await test.step('expect to be able to close a conversation from another agent', async () => {
await poOmnichannel.quickActionsRoomToolbar.closeChat();
await poOmnichannel.chats.goto();
});
await test.step('expect not to be able to remove closed room', async () => {
await expect(poOmnichannel.chats.table.findRowByName(ROOM_A)).toBeVisible();
await expect(poOmnichannel.chats.table.btnRemoveByName(ROOM_A)).not.toBeVisible();
});
});
test('OC - Monitor Role - Permission revoked', async ({ page, api }) => {
const [unitA, unitB] = units;
const [monitor] = monitors;
const poContactCenterChats = poOmnichannel.chats;
await poContactCenterChats.goto();
await test.step('expect not to be able to see chats from removed department', async () => {
await test.step('expect rooms from both departments to be visible', async () => {
await expect(poContactCenterChats.table.findRowByName(ROOM_B)).toBeVisible();
await expect(poContactCenterChats.table.findRowByName(ROOM_C)).toBeVisible();
await expect(poContactCenterChats.table.findRowByName(ROOM_D)).not.toBeVisible();
});
await test.step('expect to remove departmentB from unit', async () => {
const [departmentA] = departments.map(({ data }) => data);
await createOrUpdateUnit(api, {
id: unitA.data._id,
monitors: [{ monitorId: MONITOR, username: MONITOR }],
departments: [{ departmentId: departmentA._id }],
});
await page.reload();
});
await test.step('expect to have only room B visible', async () => {
await expect(poContactCenterChats.table.findRowByName(ROOM_B)).toBeVisible();
await expect(poContactCenterChats.table.findRowByName(ROOM_C)).not.toBeVisible();
await expect(poContactCenterChats.table.findRowByName(ROOM_D)).not.toBeVisible();
});
});
await test.step('expect not to be able to see conversations once unit is removed', async () => {
const res = await unitA.delete();
expect(res.status()).toBe(200);
await page.reload();
await expect(poContactCenterChats.table.findRowByName(ROOM_B)).not.toBeVisible();
await expect(poContactCenterChats.table.findRowByName(ROOM_C)).not.toBeVisible();
await expect(poContactCenterChats.table.findRowByName(ROOM_D)).not.toBeVisible();
});
await test.step('expect to be able to see all conversations once all units are removed', async () => {
const res = await unitB.delete();
expect(res.status()).toBe(200);
await page.reload();
await expect(poContactCenterChats.table.findRowByName(ROOM_D)).toBeVisible();
});
await test.step('expect not to be able to see chats once role is removed', async () => {
const res = await monitor.delete();
expect(res.status()).toBe(200);
await page.reload();
await expect(page.locator('p >> text="You are not authorized to view this page."')).toBeVisible();
});
await test.step('expect monitor to be automaticaly removed from unit once monitor is removed', async () => {
const { data: monitors } = await fetchUnitMonitors(api, unitA.data._id);
expect(monitors).toHaveLength(0);
});
});
});

View File

@@ -0,0 +1,82 @@
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelMonitors } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.user1.state });
test.skip(!IS_EE, 'OC - Manage Monitors > Enterprise Only');
test.describe.serial('OC - Manage Monitors', () => {
let poMonitors: OmnichannelMonitors;
test.beforeAll(async ({ api }) => {
await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/agent', { username: 'user2' }),
api.post('/livechat/users/manager', { username: 'user1' }),
]);
});
test.afterAll(async ({ api }) => {
await Promise.all([
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/manager/user1'),
api.delete('/livechat/users/agent/user2'),
api.delete('/livechat/users/manager/user2'),
]);
});
test.beforeEach(async ({ page }) => {
poMonitors = new OmnichannelMonitors(page);
await poMonitors.goto();
});
test('OC - Manager Monitors - Add monitor', async () => {
await test.step('expect to add agent as monitor', async () => {
await expect(poMonitors.table.findRowByName('user1')).not.toBeVisible();
await poMonitors.addMonitor('user1');
await expect(poMonitors.table.findRowByName('user1')).toBeVisible();
});
await test.step('expect to remove agent from monitor', async () => {
await poMonitors.removeMonitor('user1');
await expect(poMonitors.table.findRowByName('user1')).not.toBeVisible();
});
});
test('OC - Manager Monitors - Search', async () => {
await test.step('expect to add 2 monitors', async () => {
await poMonitors.addMonitor('user1');
await expect(poMonitors.table.findRowByName('user1')).toBeVisible();
await poMonitors.addMonitor('user2');
await expect(poMonitors.table.findRowByName('user2')).toBeVisible();
});
await test.step('expect to search monitor', async () => {
await expect(poMonitors.table.findRowByName('user1')).toBeVisible();
await expect(poMonitors.table.findRowByName('user2')).toBeVisible();
await poMonitors.inputSearch.fill('user1');
await expect(poMonitors.table.findRowByName('user1')).toBeVisible();
await expect(poMonitors.table.findRowByName('user2')).not.toBeVisible();
await poMonitors.inputSearch.fill('user2');
await expect(poMonitors.table.findRowByName('user1')).not.toBeVisible();
await expect(poMonitors.table.findRowByName('user2')).toBeVisible();
await poMonitors.inputSearch.fill('');
await expect(poMonitors.table.findRowByName('user1')).toBeVisible();
await expect(poMonitors.table.findRowByName('user2')).toBeVisible();
});
await test.step('expect to remove monitors', async () => {
await poMonitors.removeMonitor('user1');
await expect(poMonitors.table.findRowByName('user1')).not.toBeVisible();
await poMonitors.removeMonitor('user2');
await expect(poMonitors.table.findRowByName('user2')).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,103 @@
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
const visitor = createFakeVisitor();
const getPrioritySystemMessage = (username: string, priority: string) =>
`Priority changed: ${username} changed the priority to ${priority}`;
test.skip(!IS_EE, 'Omnichannel Priorities > Enterprise Only');
test.use({ storageState: Users.user1.state });
test.describe.serial('OC - Priorities [Sidebar]', () => {
let poHomeChannel: HomeOmnichannel;
test.beforeAll(async ({ api }) => {
(
await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }),
])
).every((res) => expect(res.status()).toBe(200));
});
test.beforeEach(async ({ page }) => {
poHomeChannel = new HomeOmnichannel(page);
});
test.beforeEach(async () => {
await poHomeChannel.goto();
});
test.beforeEach(async ({ api }) => {
await createConversation(api, { visitorName: visitor.name });
});
test.afterAll(async ({ api }) => {
(
await Promise.all([
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/manager/user1'),
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }),
])
).every((res) => expect(res.status()).toBe(200));
});
test('OC - Priorities [Sidebar] - Update conversation priority', async ({ page }) => {
const systemMessage = poHomeChannel.content.lastSystemMessageBody;
await page.emulateMedia({ reducedMotion: 'reduce' });
await test.step('expect to change inquiry priority using sidebar menu', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitor.name).click();
await expect(poHomeChannel.content.btnTakeChat).toBeVisible();
await expect(poHomeChannel.roomInfo.getLabel('Priority')).not.toBeVisible();
await poHomeChannel.sidebar.selectPriority(visitor.name, 'Lowest');
await systemMessage.locator(`text="${getPrioritySystemMessage('user1', 'Lowest')}"`).waitFor();
await expect(poHomeChannel.roomInfo.getLabel('Priority')).toBeVisible();
await expect(poHomeChannel.sidebar.getBadgeIndicator(visitor.name, 'Lowest')).toBeVisible();
await expect(poHomeChannel.roomInfo.getInfo('Lowest')).toBeVisible();
await poHomeChannel.sidebar.selectPriority(visitor.name, 'Highest');
await systemMessage.locator(`text="${getPrioritySystemMessage('user1', 'Highest')}"`).waitFor();
await expect(poHomeChannel.roomInfo.getInfo('Highest')).toBeVisible();
await expect(poHomeChannel.sidebar.getBadgeIndicator(visitor.name, 'Highest')).toBeVisible();
await poHomeChannel.sidebar.selectPriority(visitor.name, 'Unprioritized');
await systemMessage.locator(`text="${getPrioritySystemMessage('user1', 'Unprioritized')}"`).waitFor();
await expect(poHomeChannel.roomInfo.getLabel('Priority')).not.toBeVisible();
await expect(poHomeChannel.sidebar.getBadgeIndicator(visitor.name, 'Unprioritized')).not.toBeVisible();
});
await test.step('expect to change subscription priority using sidebar menu', async () => {
await poHomeChannel.content.btnTakeChat.click();
await systemMessage.locator('text="joined the channel"').waitFor();
await page.waitForTimeout(500);
await expect(poHomeChannel.roomInfo.getLabel('Priority')).not.toBeVisible();
await poHomeChannel.sidebar.selectPriority(visitor.name, 'Lowest');
await systemMessage.locator(`text="${getPrioritySystemMessage('user1', 'Lowest')}"`).waitFor();
await expect(poHomeChannel.roomInfo.getLabel('Priority')).toBeVisible();
await expect(poHomeChannel.roomInfo.getInfo('Lowest')).toBeVisible();
await expect(poHomeChannel.sidebar.getBadgeIndicator(visitor.name, 'Lowest')).toBeVisible();
await poHomeChannel.sidebar.selectPriority(visitor.name, 'Highest');
await systemMessage.locator(`text="${getPrioritySystemMessage('user1', 'Highest')}"`).waitFor();
await expect(poHomeChannel.roomInfo.getInfo('Highest')).toBeVisible();
await expect(poHomeChannel.sidebar.getBadgeIndicator(visitor.name, 'Highest')).toBeVisible();
await poHomeChannel.sidebar.selectPriority(visitor.name, 'Unprioritized');
await systemMessage.locator(`text="${getPrioritySystemMessage('user1', 'Unprioritized')}"`).waitFor();
await expect(poHomeChannel.roomInfo.getLabel('Priority')).not.toBeVisible();
await expect(poHomeChannel.roomInfo.getInfo('Unprioritized')).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,127 @@
import { faker } from '@faker-js/faker';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelPriorities } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
const PRIORITY_NAME = faker.person.firstName();
const ERROR = {
fieldNameRequired: 'Name required',
};
test.skip(!IS_EE, 'Omnichannel Priorities > Enterprise Only');
test.use({ storageState: Users.user1.state });
test.describe.serial('Omnichannel Priorities', () => {
let poOmnichannelPriorities: OmnichannelPriorities;
test.beforeAll(async ({ api }) => {
await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
]);
});
test.beforeEach(async ({ page }) => {
poOmnichannelPriorities = new OmnichannelPriorities(page);
await poOmnichannelPriorities.goto();
});
test.afterAll(async ({ api }) => {
await Promise.all([api.delete('/livechat/users/agent/user1'), api.delete('/livechat/users/manager/user1')]);
});
test('Manage Priorities', async () => {
await test.step('All default priorities should be visible', async () => {
await Promise.all([
expect(poOmnichannelPriorities.findPriority('Highest')).toBeVisible(),
expect(poOmnichannelPriorities.findPriority('High')).toBeVisible(),
expect(poOmnichannelPriorities.findPriority('Medium')).toBeVisible(),
expect(poOmnichannelPriorities.findPriority('Low')).toBeVisible(),
expect(poOmnichannelPriorities.findPriority('Lowest')).toBeVisible(),
]);
});
await test.step('Add new priority', async () => {
await expect(poOmnichannelPriorities.btnReset).toBeDisabled();
await poOmnichannelPriorities.findPriority('Highest').click();
await test.step('default state', async () => {
await Promise.all([
expect(poOmnichannelPriorities.editPriority.btnSave).not.toBeDisabled(),
expect(poOmnichannelPriorities.editPriority.inputName).toHaveValue('Highest'),
]);
});
await test.step('field name is required', async () => {
await poOmnichannelPriorities.editPriority.inputName.fill('any_text');
await poOmnichannelPriorities.editPriority.inputName.clear();
await poOmnichannelPriorities.editPriority.btnSave.click();
await expect(poOmnichannelPriorities.editPriority.errorMessage(ERROR.fieldNameRequired)).toBeVisible();
});
await test.step('should trim field name and show error if only spaces', async () => {
await poOmnichannelPriorities.editPriority.inputName.fill(' ');
await poOmnichannelPriorities.editPriority.btnSave.click();
await expect(poOmnichannelPriorities.editPriority.errorMessage(ERROR.fieldNameRequired)).toBeVisible();
});
await test.step('edit and save priority', async () => {
await poOmnichannelPriorities.editPriority.inputName.fill(PRIORITY_NAME);
await expect(poOmnichannelPriorities.editPriority.errorMessage(ERROR.fieldNameRequired)).not.toBeVisible();
await poOmnichannelPriorities.editPriority.save();
await Promise.all([
expect(poOmnichannelPriorities.findPriority(PRIORITY_NAME)).toBeVisible(),
expect(poOmnichannelPriorities.findPriority('Highest')).not.toBeVisible(),
]);
});
});
await test.step('Reset priority', async () => {
await test.step('reset individual', async () => {
await poOmnichannelPriorities.findPriority(PRIORITY_NAME).click();
await expect(poOmnichannelPriorities.editPriority.btnReset).toBeVisible();
await poOmnichannelPriorities.editPriority.btnReset.click();
await Promise.all([
expect(poOmnichannelPriorities.editPriority.inputName).toHaveValue('Highest'),
expect(poOmnichannelPriorities.editPriority.btnReset).toBeDisabled(),
]);
await expect(poOmnichannelPriorities.editPriority.btnSave).toBeEnabled();
await poOmnichannelPriorities.editPriority.save();
await expect(poOmnichannelPriorities.findPriority('Highest')).toBeVisible();
await expect(poOmnichannelPriorities.btnReset).toBeDisabled();
});
await test.step('reset all', async () => {
await poOmnichannelPriorities.findPriority('Highest').click();
await poOmnichannelPriorities.editPriority.inputName.fill(PRIORITY_NAME);
await Promise.all([
expect(poOmnichannelPriorities.editPriority.btnReset).toBeVisible(),
expect(poOmnichannelPriorities.editPriority.btnSave).toBeEnabled(),
]);
await poOmnichannelPriorities.editPriority.save();
await Promise.all([
expect(poOmnichannelPriorities.editPriority.inputName).not.toBeVisible(),
expect(poOmnichannelPriorities.findPriority(PRIORITY_NAME)).toBeVisible(),
expect(poOmnichannelPriorities.findPriority('Highest')).not.toBeVisible(),
]);
await expect(poOmnichannelPriorities.btnReset).toBeEnabled();
await poOmnichannelPriorities.resetPriorities();
await Promise.all([
expect(poOmnichannelPriorities.btnReset).toBeDisabled(),
expect(poOmnichannelPriorities.findPriority(PRIORITY_NAME)).not.toBeVisible(),
expect(poOmnichannelPriorities.findPriority('Highest')).toBeVisible(),
]);
});
});
});
});

View File

@@ -0,0 +1,364 @@
import type { Route } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelReports } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
const ENDPOINTS = {
BY_STATUS: /\/v1\/livechat\/analytics\/dashboards\/conversations-by-status/,
BY_SOURCE: /\/v1\/livechat\/analytics\/dashboards\/conversations-by-source/,
BY_DEPARTMENT: /\/v1\/livechat\/analytics\/dashboards\/conversations-by-department/,
BY_TAGS: /\/v1\/livechat\/analytics\/dashboards\/conversations-by-tags/,
BY_AGENT: /\/v1\/livechat\/analytics\/dashboards\/conversations-by-agent/,
};
test.skip(!IS_EE, 'Omnichannel Reports > Enterprise Only');
test.use({ storageState: Users.user1.state });
test.describe.serial('Omnichannel Reports', () => {
let poReports: OmnichannelReports;
test.beforeAll(async ({ api }) => {
const requests = await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
]);
await expect(requests.every((request) => request.status() === 200)).toBe(true);
});
test.beforeEach(async ({ page }) => {
poReports = new OmnichannelReports(page);
});
test.beforeEach(async () => {
await poReports.goto();
});
test.afterAll(async ({ api }) => {
await Promise.all([api.delete('/livechat/users/agent/user1'), api.delete('/livechat/users/manager/user1')]);
});
test('Status Section', async ({ page }) => {
await test.step('Empty state', async () => {
await page.route(ENDPOINTS.BY_STATUS, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
await poReports.statusSection.selectPeriod('this week');
await expect(poReports.statusSection.txtStateTitle).toHaveText('No data available for the selected period');
await expect(poReports.statusSection.txtStateSubtitle).toHaveText('This chart will update as soon as conversations start.');
await expect(poReports.statusSection.txtSummary).toHaveText('0 conversations, this week');
});
await test.step('Error state', async () => {
await page.route(ENDPOINTS.BY_STATUS, async (route: Route) => {
await route.abort();
});
await poReports.statusSection.selectPeriod('this month');
await expect(poReports.statusSection.element).toHaveAttribute('aria-busy', 'false');
await expect(poReports.statusSection.btnRetry).toBeVisible();
await expect(poReports.statusSection.txtStateTitle).toHaveText('Something went wrong');
await test.step('Retry', async () => {
await page.route(ENDPOINTS.BY_STATUS, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
const responsePromise = page.waitForResponse(ENDPOINTS.BY_STATUS);
await poReports.statusSection.btnRetry.click();
await responsePromise;
});
});
await test.step('Render data', async () => {
const mock = {
data: [
{ label: 'Open', value: 25 },
{ label: 'Queued', value: 25 },
{ label: 'Closed', value: 25 },
{ label: 'On_Hold', value: 25 },
],
total: 100,
};
await page.route(ENDPOINTS.BY_STATUS, async (route: Route) => {
const response = await route.fetch();
await route.fulfill({ response, json: mock });
});
await poReports.statusSection.selectPeriod('this year');
await expect(poReports.statusSection.legendItem(`Closed 25 (25%)`)).toBeVisible();
await expect(poReports.statusSection.legendItem(`Open 25 (25%)`)).toBeVisible();
await expect(poReports.statusSection.legendItem(`Queued 25 (25%)`)).toBeVisible();
await expect(poReports.statusSection.legendItem(`On hold 25 (25%)`)).toBeVisible();
await expect(poReports.statusSection.txtSummary).toHaveText('100 conversations, this year');
});
});
test('Channels Section', async ({ page }) => {
await test.step('Empty state', async () => {
await page.route(ENDPOINTS.BY_SOURCE, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
await poReports.channelsSection.selectPeriod('this week');
await expect(poReports.channelsSection.txtStateTitle).toHaveText('No data available for the selected period');
await expect(poReports.channelsSection.txtStateSubtitle).toHaveText('This chart shows the most used channels.');
await expect(poReports.channelsSection.txtSummary).toHaveText('0 conversations, this week');
});
await test.step('Error state', async () => {
await page.route(ENDPOINTS.BY_SOURCE, async (route: Route) => {
await route.abort();
});
await poReports.channelsSection.selectPeriod('last 15 days');
await expect(poReports.channelsSection.element).toHaveAttribute('aria-busy', 'false');
await expect(poReports.channelsSection.btnRetry).toBeVisible();
await expect(poReports.channelsSection.txtStateTitle).toHaveText('Something went wrong');
await test.step('Retry', async () => {
await page.route(ENDPOINTS.BY_SOURCE, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
const responsePromise = page.waitForResponse(ENDPOINTS.BY_SOURCE);
await poReports.channelsSection.btnRetry.click();
await responsePromise;
});
});
await test.step('Render data', async () => {
const mock = {
data: [
{ label: 'Channel 1', value: 50 },
{ label: 'Channel 2', value: 50 },
],
total: 100,
};
await page.route(ENDPOINTS.BY_SOURCE, async (route: Route) => {
const response = await route.fetch();
await route.fulfill({ response, json: mock });
});
await poReports.channelsSection.selectPeriod('this year');
await expect(poReports.channelsSection.legendItem(`Channel 1 50 (50%)`)).toBeVisible();
await expect(poReports.channelsSection.legendItem(`Channel 2 50 (50%)`)).toBeVisible();
await expect(poReports.channelsSection.txtSummary).toHaveText('100 conversations, this year');
});
await test.step('More than 5 channels', async () => {
const mock = {
data: [
{ label: 'Channel 1', value: 15 },
{ label: 'Channel 2', value: 15 },
{ label: 'Channel 3', value: 15 },
{ label: 'Channel 4', value: 15 },
{ label: 'Channel 5', value: 15 },
{ label: 'Channel 6', value: 15 },
{ label: 'Channel 7', value: 15 },
{ label: 'Channel 8', value: 15 },
],
total: 120,
};
await page.route(ENDPOINTS.BY_SOURCE, async (route: Route) => {
const response = await route.fetch();
await route.fulfill({ response, json: mock });
});
await poReports.channelsSection.selectPeriod('last 6 months');
await expect(poReports.channelsSection.legendItem(`Others 45 (37.5%)`)).toBeVisible();
await expect(poReports.channelsSection.txtSummary).toHaveText('120 conversations, last 6 months');
});
});
test('Departments Section', async ({ page }) => {
await test.step('Empty state', async () => {
await page.route(ENDPOINTS.BY_DEPARTMENT, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
await poReports.departmentsSection.selectPeriod('this week');
await expect(poReports.departmentsSection.txtStateTitle).toHaveText('No data available for the selected period');
await expect(poReports.departmentsSection.txtStateSubtitle).toHaveText(
'This chart displays the departments that receive the most conversations.',
);
await expect(poReports.departmentsSection.txtSummary).toHaveText('0 departments and 0 conversations, this week');
});
await test.step('Error state', async () => {
await page.route(ENDPOINTS.BY_DEPARTMENT, async (route: Route) => {
await route.abort();
});
await poReports.departmentsSection.selectPeriod('this month');
await expect(poReports.departmentsSection.element).toHaveAttribute('aria-busy', 'false');
await expect(poReports.departmentsSection.btnRetry).toBeVisible();
await expect(poReports.departmentsSection.txtStateTitle).toHaveText('Something went wrong');
await test.step('Retry', async () => {
await page.route(ENDPOINTS.BY_DEPARTMENT, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
const responsePromise = page.waitForResponse(ENDPOINTS.BY_DEPARTMENT);
await poReports.departmentsSection.btnRetry.click();
await responsePromise;
});
});
await test.step('Render data', async () => {
const mock = {
data: [
{ label: 'Department 1', value: 10 },
{ label: 'Department 2', value: 20 },
{ label: 'Department 3', value: 30 },
{ label: 'Department 4', value: 40 },
],
total: 100,
unspecified: 42,
};
await page.route(ENDPOINTS.BY_DEPARTMENT, async (route: Route) => {
const response = await route.fetch();
await route.fulfill({ response, json: mock });
});
await poReports.departmentsSection.selectPeriod('last 6 months');
await expect(poReports.departmentsSection.chartItem('Department 1', 10)).toBeVisible();
await expect(poReports.departmentsSection.chartItem('Department 2', 20)).toBeVisible();
await expect(poReports.departmentsSection.chartItem('Department 3', 30)).toBeVisible();
await expect(poReports.departmentsSection.chartItem('Department 4', 40)).toBeVisible();
await expect(poReports.departmentsSection.txtSummary).toHaveText(
'4 departments and 100 conversations, last 6 months (42 without department)',
);
});
});
test('Tags Section', async ({ page }) => {
await test.step('Empty state', async () => {
await page.route(ENDPOINTS.BY_TAGS, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
await poReports.tagsSection.selectPeriod('this week');
await expect(poReports.tagsSection.txtStateTitle).toHaveText('No data available for the selected period');
await expect(poReports.tagsSection.txtStateSubtitle).toHaveText('This chart shows the most frequently used tags.');
await expect(poReports.tagsSection.txtSummary).toHaveText('0 tags and 0 conversations, this week');
});
await test.step('Error state', async () => {
await page.route(ENDPOINTS.BY_TAGS, async (route: Route) => {
await route.abort();
});
await poReports.tagsSection.selectPeriod('this month');
await expect(poReports.tagsSection.element).toHaveAttribute('aria-busy', 'false');
await expect(poReports.tagsSection.btnRetry).toBeVisible();
await expect(poReports.tagsSection.txtStateTitle).toHaveText('Something went wrong');
await test.step('Retry', async () => {
await page.route(ENDPOINTS.BY_TAGS, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
const responsePromise = page.waitForResponse(ENDPOINTS.BY_TAGS);
await poReports.tagsSection.btnRetry.click();
await responsePromise;
});
});
await test.step('Render data', async () => {
const mock = {
data: [
{ label: 'Tag 1', value: 10 },
{ label: 'Tag 2', value: 20 },
{ label: 'Tag 3', value: 30 },
{ label: 'Tag 4', value: 40 },
],
total: 100,
unspecified: 42,
};
await page.route(ENDPOINTS.BY_TAGS, async (route: Route) => {
const response = await route.fetch();
await route.fulfill({ response, json: mock });
});
await poReports.tagsSection.selectPeriod('last 6 months');
await expect(poReports.tagsSection.chartItem('Tag 1', 10)).toBeVisible();
await expect(poReports.tagsSection.chartItem('Tag 2', 20)).toBeVisible();
await expect(poReports.tagsSection.chartItem('Tag 3', 30)).toBeVisible();
await expect(poReports.tagsSection.chartItem('Tag 4', 40)).toBeVisible();
await expect(poReports.tagsSection.txtSummary).toHaveText('4 tags and 100 conversations, last 6 months (42 without tags)');
});
});
test('Agents Section', async ({ page }) => {
await test.step('Empty state', async () => {
await page.route(ENDPOINTS.BY_AGENT, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
await poReports.agentsSection.selectPeriod('this week');
await expect(poReports.agentsSection.txtStateTitle).toHaveText('No data available for the selected period');
await expect(poReports.agentsSection.txtStateSubtitle).toHaveText(
'This chart displays which agents receive the highest volume of conversations.',
);
await expect(poReports.agentsSection.txtSummary).toHaveText('0 agents and 0 conversations, this week');
});
await test.step('Error state', async () => {
await page.route(ENDPOINTS.BY_AGENT, async (route: Route) => {
await route.abort();
});
await poReports.agentsSection.selectPeriod('this month');
await expect(poReports.agentsSection.element).toHaveAttribute('aria-busy', 'false');
await expect(poReports.agentsSection.btnRetry).toBeVisible();
await expect(poReports.agentsSection.txtStateTitle).toHaveText('Something went wrong');
await test.step('Retry', async () => {
await page.route(ENDPOINTS.BY_AGENT, async (route: Route) => {
await route.fulfill({ json: { data: [], total: 0 } });
});
const responsePromise = page.waitForResponse(ENDPOINTS.BY_AGENT);
await poReports.agentsSection.btnRetry.click();
await responsePromise;
});
});
await test.step('Render data', async () => {
const mock = {
data: [
{ label: 'Agent 1', value: 10 },
{ label: 'Agent 2', value: 20 },
{ label: 'Agent 3', value: 30 },
{ label: 'Agent 4', value: 40 },
],
total: 100,
unspecified: 42,
};
await page.route(ENDPOINTS.BY_AGENT, async (route: Route) => {
const response = await route.fetch();
await route.fulfill({ response, json: mock });
});
await poReports.agentsSection.selectPeriod('last 6 months');
await expect(poReports.agentsSection.txtSummary).toHaveText('4 agents and 100 conversations, last 6 months (42 without assignee)');
await expect(poReports.agentsSection.chartItem('Agent 1', 10)).toBeVisible();
await expect(poReports.agentsSection.chartItem('Agent 2', 20)).toBeVisible();
await expect(poReports.agentsSection.chartItem('Agent 3', 30)).toBeVisible();
await expect(poReports.agentsSection.chartItem('Agent 4', 40)).toBeVisible();
await expect(poReports.agentsSection.findRowByName('Agent 1')).toBeVisible();
await expect(poReports.agentsSection.findRowByName('Agent 2')).toBeVisible();
await expect(poReports.agentsSection.findRowByName('Agent 3')).toBeVisible();
await expect(poReports.agentsSection.findRowByName('Agent 4')).toBeVisible();
});
});
});

View File

@@ -0,0 +1,229 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { setSettingValueById } from '../utils';
import { createAgent } from '../utils/omnichannel/agents';
import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments';
import { createManager } from '../utils/omnichannel/managers';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.user1.state });
test.describe('OC - Forwarding to away departments (EE)', () => {
test.skip(!IS_EE, 'Enterprise Edition Only');
let poHomeOmnichannelOnlineAgent: HomeOmnichannel;
let poHomeOmnichannelAwayAgent: HomeOmnichannel;
let omnichannelPage: Page | undefined;
let manager: Awaited<ReturnType<typeof createManager>>;
let onlineAgent: Awaited<ReturnType<typeof createAgent>>;
let awayAgent: Awaited<ReturnType<typeof createAgent>>;
let initialDepartment: Awaited<ReturnType<typeof createDepartment>>;
let forwardToOfflineDepartment: Awaited<ReturnType<typeof createDepartment>>;
let conversation: Awaited<ReturnType<typeof createConversation>>;
let visitor: { name: string; email: string };
test.beforeAll(async ({ api }) => {
[manager, onlineAgent, awayAgent, initialDepartment, forwardToOfflineDepartment] = await Promise.all([
createManager(api, 'user1'),
createAgent(api, 'user1'),
createAgent(api, 'user2'),
createDepartment(api, { name: 'Initial Dept' }),
createDepartment(api, { name: 'Forward Dept', allowReceiveForwardOffline: true }),
]);
await expect(manager.response).toBeOK();
await expect(onlineAgent.response).toBeOK();
await expect(awayAgent.response).toBeOK();
await expect(initialDepartment.response).toBeOK();
await expect(forwardToOfflineDepartment.response).toBeOK();
const responses = await Promise.all([
setSettingValueById(api, 'Accounts_Default_User_Preferences_idleTimeLimit', 300),
setSettingValueById(api, 'Omnichannel_enable_department_removal', true),
setSettingValueById(api, 'Livechat_enabled_when_agent_idle', false),
addAgentToDepartment(api, { department: initialDepartment.data, agentId: 'user1' }),
addAgentToDepartment(api, { department: forwardToOfflineDepartment.data, agentId: 'user2' }),
]);
for (const response of responses) {
await expect(response).toBeOK();
}
});
test.beforeEach(async ({ page, browser, api }) => {
visitor = createFakeVisitor();
// Online Agent window opens with idleTimeLimit of 300 by default, therefore it will remain online
poHomeOmnichannelOnlineAgent = new HomeOmnichannel(page);
await poHomeOmnichannelOnlineAgent.goto();
await expect(await setSettingValueById(api, 'Accounts_Default_User_Preferences_idleTimeLimit', 1)).toBeOK();
// Away Agent window opens with idleTimeLimit of 1, therefore after a second it will turn away
({ page: omnichannelPage } = await createAuxContext(browser, Users.user2, '/', false));
poHomeOmnichannelAwayAgent = new HomeOmnichannel(omnichannelPage);
await poHomeOmnichannelAwayAgent.waitForHome();
await expect(poHomeOmnichannelAwayAgent.navbar.getUserStatusBadge('away')).toBeVisible();
});
test.afterEach(async ({ api }) => {
if (omnichannelPage) {
await omnichannelPage.context().close();
}
await expect(await setSettingValueById(api, 'Livechat_waiting_queue', false)).toBeOK();
await expect(await setSettingValueById(api, 'Accounts_Default_User_Preferences_idleTimeLimit', 300)).toBeOK();
});
test.afterAll(async ({ api }) => {
const responses = await Promise.all([
initialDepartment.delete(),
forwardToOfflineDepartment.delete(),
manager.delete(),
onlineAgent.delete(),
awayAgent.delete(),
setSettingValueById(api, 'Livechat_Routing_Method', 'Auto_Selection'),
setSettingValueById(api, 'Livechat_enabled_when_agent_idle', true),
setSettingValueById(api, 'Omnichannel_enable_department_removal', false),
]);
for (const response of responses) {
await expect(response).toBeOK();
}
});
test('when manager forward to offline (agent away, accept when agent idle off) department the inquiry should be set to the queue', async ({
api,
}) => {
await test.step('Setup routing settings', async () => {
await expect(await setSettingValueById(api, 'Livechat_Routing_Method', 'Manual_Selection')).toBeOK();
});
await test.step('Visitor initiates chat', async () => {
conversation = await createConversation(api, { visitorName: visitor.name, agentId: onlineAgent.data._id });
});
await test.step('Manager forwards chat', async () => {
await poHomeOmnichannelOnlineAgent.sidebar.getSidebarItemByName(visitor.name).click();
await poHomeOmnichannelOnlineAgent.quickActionsRoomToolbar.forwardChat();
await poHomeOmnichannelOnlineAgent.content.forwardChatModal.selectDepartment('Forward Dept');
await poHomeOmnichannelOnlineAgent.content.forwardChatModal.btnForward.click();
await expect(poHomeOmnichannelOnlineAgent.content.forwardChatModal.btnForward).not.toBeVisible();
});
await test.step('Check inquiry status via API is queued', async () => {
const inquiryResp = await api.get(`/livechat/inquiries.getOne?roomId=${conversation.data.room._id}`);
await expect(inquiryResp).toBeOK();
const inquiryBody = await inquiryResp.json();
expect(inquiryBody.inquiry.status).toBe('queued');
expect(inquiryBody.inquiry.department).toBe(forwardToOfflineDepartment.data._id);
});
});
test('when manager forward to a department while waiting_queue is active and allowReceiveForwardOffline is true, chat should end in departments queue', async ({
api,
}) => {
await test.step('Setup routing settings', async () => {
await expect(await setSettingValueById(api, 'Livechat_Routing_Method', 'Auto_Selection')).toBeOK();
});
await test.step('Visitor initiates chat', async () => {
conversation = await createConversation(api, { visitorName: visitor.name, agentId: onlineAgent.data._id });
});
await test.step('Manager enables queue and forwards chat', async () => {
await poHomeOmnichannelOnlineAgent.sidebar.getSidebarItemByName(visitor.name).click();
await expect(await setSettingValueById(api, 'Livechat_waiting_queue', true)).toBeOK();
await poHomeOmnichannelOnlineAgent.quickActionsRoomToolbar.forwardChat();
await poHomeOmnichannelOnlineAgent.content.forwardChatModal.selectDepartment('Forward Dept');
await poHomeOmnichannelOnlineAgent.content.forwardChatModal.btnForward.click();
await expect(poHomeOmnichannelOnlineAgent.content.forwardChatModal.btnForward).not.toBeVisible();
});
await test.step('Check inquiry status via API is queued', async () => {
const inquiryResp = await api.get(`/livechat/inquiries.getOne?roomId=${conversation.data.room._id}`);
await expect(inquiryResp).toBeOK();
const inquiryBody = await inquiryResp.json();
expect(inquiryBody.inquiry.status).toBe('queued');
expect(inquiryBody.inquiry.department).toBe(forwardToOfflineDepartment.data._id);
});
});
test('when manager forward to a department while waiting_queue is active and allowReceiveForwardOffline is false, transfer should fail', async ({
api,
}) => {
await test.step('Setup routing and department settings', async () => {
await expect(await setSettingValueById(api, 'Livechat_Routing_Method', 'Auto_Selection')).toBeOK();
await expect(
await api.put(`/livechat/department/${forwardToOfflineDepartment.data._id}`, {
department: { ...forwardToOfflineDepartment.data, allowReceiveForwardOffline: false },
}),
).toBeOK();
});
await test.step('Visitor initiates chat', async () => {
conversation = await createConversation(api, { visitorName: visitor.name, agentId: onlineAgent.data._id });
});
await test.step('Manager attempts to forward and sees error', async () => {
await poHomeOmnichannelOnlineAgent.sidebar.getSidebarItemByName(visitor.name).click();
await expect(await setSettingValueById(api, 'Livechat_waiting_queue', true)).toBeOK();
await poHomeOmnichannelOnlineAgent.quickActionsRoomToolbar.forwardChat();
await poHomeOmnichannelOnlineAgent.content.forwardChatModal.selectDepartment('Forward Dept');
await poHomeOmnichannelOnlineAgent.content.forwardChatModal.btnForward.click();
await expect(poHomeOmnichannelOnlineAgent.page.locator('role=alert')).toContainText(
'No agents are available for service on this department.',
);
});
await test.step('Restore department setting', async () => {
await expect(
await api.put(`/livechat/department/${forwardToOfflineDepartment.data._id}`, {
department: { ...forwardToOfflineDepartment.data, allowReceiveForwardOffline: true },
}),
).toBeOK();
});
});
test('when manager forward to online (agent away, accept when agent idle on) department the inquiry should not be set to the queue', async ({
api,
}) => {
await test.step('Setup routing settings', async () => {
await expect(await setSettingValueById(api, 'Livechat_Routing_Method', 'Auto_Selection')).toBeOK();
});
await test.step('Visitor initiates chat', async () => {
conversation = await createConversation(api, { visitorName: visitor.name, agentId: onlineAgent.data._id });
});
await test.step('Set Livechat_enabled_when_agent_idle to true', async () => {
// We set Livechat_enabled_when_agent_idle to true here in order to not assign the away agent in the previous step
await expect(await setSettingValueById(api, 'Livechat_enabled_when_agent_idle', true)).toBeOK();
});
await test.step('Manager forwards chat successfully', async () => {
await poHomeOmnichannelOnlineAgent.sidebar.getSidebarItemByName(visitor.name).click();
await poHomeOmnichannelOnlineAgent.quickActionsRoomToolbar.forwardChat();
await poHomeOmnichannelOnlineAgent.content.forwardChatModal.selectDepartment('Forward Dept');
await poHomeOmnichannelOnlineAgent.content.forwardChatModal.btnForward.click();
await expect(poHomeOmnichannelOnlineAgent.content.forwardChatModal.btnForward).not.toBeVisible();
});
await test.step('Check room routing via API serves to away agent', async () => {
const inquiryResp = await api.get(`/livechat/inquiries.getOne?roomId=${conversation.data.room._id}`);
await expect(inquiryResp).toBeOK();
const inquiryBody = await inquiryResp.json();
expect(inquiryBody.inquiry.status).toBe('taken');
expect(inquiryBody.inquiry.department).toBe(forwardToOfflineDepartment.data._id);
});
});
});

View File

@@ -0,0 +1,84 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.skip(!IS_EE, 'Export transcript as PDF > Enterprie Only');
test.describe('omnichannel- export chat transcript as PDF', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeOmnichannel };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
// Set user user 1 as manager and agent
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeChannel: new HomeOmnichannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterAll(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await api.delete('/livechat/users/manager/user1');
await agent.page.close();
});
test('Export PDF transcript', async ({ page }) => {
await test.step('Expect send a message as a visitor', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => {
await new Promise((resolve) => setTimeout(resolve, 5000));
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('Expect to be not able send transcript as PDF', async () => {
await agent.poHomeChannel.content.btnSendTranscript.click();
await agent.poHomeChannel.content.btnSendTranscriptAsPDF.hover();
await expect(agent.poHomeChannel.content.btnSendTranscriptAsPDF).toHaveAttribute('aria-disabled', 'true');
});
await test.step('Expect chat to be closed', async () => {
await agent.poHomeChannel.quickActionsRoomToolbar.closeChat({ downloadPDF: true });
});
// Exported PDF can be downloaded from rocket.cat room
await test.step('Expect to have exported PDF in rocket.cat', async () => {
await page.waitForTimeout(3000);
await agent.poHomeChannel.navbar.openChat('rocket.cat');
await expect(agent.poHomeChannel.content.lastUserMessage.getByText('PDF Transcript successfully generated')).toBeVisible();
await expect(agent.poHomeChannel.content.lastUserMessage.getByRole('link', { name: 'Transcript' })).toBeVisible();
});
// PDF can be exported from Omnichannel Contact Center
await test.step('Expect to have exported PDF in rocket.cat', async () => {
await agent.poHomeChannel.navbar.btnContactCenter.click();
await agent.poHomeChannel.transcript.contactCenterChats.click();
await agent.poHomeChannel.transcript.contactCenterSearch.type(newVisitor.name);
await page.waitForTimeout(3000);
await agent.poHomeChannel.transcript.firstRow.click();
await agent.poHomeChannel.transcript.btnOpenChat.click();
await agent.poHomeChannel.content.btnSendTranscript.click();
await expect(agent.poHomeChannel.content.btnSendTranscriptAsPDF).toHaveAttribute('aria-disabled', 'false');
await agent.poHomeChannel.content.btnSendTranscriptAsPDF.click();
await agent.poHomeChannel.toastMessage.waitForDisplay();
});
});
});

View File

@@ -0,0 +1,63 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeChannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe('omnichannel-transcript', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeChannel };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
// Set user user 1 as manager and agent
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeChannel: new HomeChannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterAll(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await api.delete('/livechat/users/manager/user1');
await agent.page.close();
});
test('Receiving a message from visitor', async () => {
await test.step('Expect send a message as a visitor', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
});
await test.step('Expect to have 1 omnichannel assigned to agent 1', async () => {
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
});
await test.step('Expect to be able to send transcript to email', async () => {
await agent.poHomeChannel.content.btnSendTranscript.click();
await agent.poHomeChannel.content.btnSendTranscriptToEmail.click();
await agent.poHomeChannel.content.btnModalConfirm.click();
await agent.poHomeChannel.toastMessage.waitForDisplay();
});
await test.step('Expect to be not able send transcript as PDF', async () => {
test.skip(!IS_EE, 'Enterprise Only');
await agent.poHomeChannel.content.btnSendTranscript.click();
await agent.poHomeChannel.content.btnSendTranscriptAsPDF.hover();
await expect(agent.poHomeChannel.content.btnSendTranscriptAsPDF).toHaveAttribute('aria-disabled', 'true');
});
});
});

View File

@@ -0,0 +1,148 @@
import {
OmnichannelSortingMechanismSettingType,
type IOmnichannelServiceLevelAgreements,
type Serialized,
} from '@rocket.chat/core-typings';
import { createFakeVisitor } from '../../mocks/data';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { createConversation } from '../utils/omnichannel/rooms';
import { createSLA } from '../utils/omnichannel/sla';
import { test, expect } from '../utils/test';
const visitorA = createFakeVisitor();
const visitorB = createFakeVisitor();
const visitorC = createFakeVisitor();
test.skip(!IS_EE, 'Omnichannel SLAs > Enterprise Only');
test.use({ storageState: Users.user1.state });
test.describe('OC - SLA Policies [Sidebar]', () => {
let poHomeChannel: HomeOmnichannel;
let conversations: Awaited<ReturnType<typeof createConversation>>[] = [];
let slas: Serialized<Omit<IOmnichannelServiceLevelAgreements, '_updatedAt'>>[] = [];
test.beforeAll('create SLAs', async ({ api }) => {
slas = await Promise.all([
createSLA(api, { name: 'Very Urgent', dueTimeInMinutes: 1 }),
createSLA(api, { name: 'Urgent', dueTimeInMinutes: 10 }),
createSLA(api, { name: 'Not Urgent', dueTimeInMinutes: 30 }),
]);
});
test.beforeAll(async ({ api }) => {
(
await Promise.all([
// Create agent and manager
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
// Settings
api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }),
api.post('/settings/Omnichannel_sorting_mechanism', { value: OmnichannelSortingMechanismSettingType.SLAs }),
])
).every((res) => expect(res.status()).toBe(200));
});
test.beforeEach(async ({ page }) => {
poHomeChannel = new HomeOmnichannel(page);
});
test.beforeEach(async () => {
await poHomeChannel.goto();
});
test.beforeEach(async ({ api }) => {
conversations = await Promise.all([
createConversation(api, { visitorName: visitorA.name, agentId: 'user1' }),
createConversation(api, { visitorName: visitorB.name, agentId: 'user1' }),
createConversation(api, { visitorName: visitorC.name, agentId: 'user1' }),
]);
});
test.afterAll('delete SLAs', async ({ api }) => {
const responses = await Promise.all(slas.map((sla) => api.delete(`/livechat/sla/${sla._id}`)));
responses.every((res) => expect(res.status()).toBe(200));
});
test.afterAll(async ({ api }) => {
// Delete conversations
await Promise.all(conversations.map((conversation) => conversation.delete()));
(
await Promise.all([
// Delete agent and manager
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/manager/user1'),
// Reset settings
api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }),
api.post('/settings/Omnichannel_sorting_mechanism', { value: OmnichannelSortingMechanismSettingType.Timestamp }),
])
).every((res) => expect(res.status()).toBe(200));
});
test('OC - SLA Policies [Sidebar] - Update conversation SLA Policy', async () => {
await test.step('expect to change room SLA policy to "Not urgent"', async () => {
await test.step('expect to open room and room info to be visible', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitorA.name).click();
await expect(poHomeChannel.roomInfo.root).toBeVisible();
});
await test.step('expect to update room SLA policy', async () => {
await expect(poHomeChannel.roomInfo.getInfoByLabel('SLA Policy')).not.toBeVisible();
await poHomeChannel.roomInfo.btnEdit.click();
await poHomeChannel.editRoomInfo.selectSLA('Not Urgent');
await poHomeChannel.editRoomInfo.btnSave.click();
});
await test.step('expect SLA to have been updated in the room info and queue order to be correct', async () => {
await expect(poHomeChannel.roomInfo.getInfoByLabel('SLA Policy')).toHaveText('Not Urgent');
await expect(poHomeChannel.sidebar.getSidebarListItem(visitorA.name)).toBeVisible();
await expect(poHomeChannel.sidebar.getSidebarListItem(visitorA.name)).toHaveAttribute('data-index', '1');
});
});
await test.step('expect to change room SLA policy to "Urgent"', async () => {
await test.step('expect to open room and room info to be visible', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitorB.name).click();
await expect(poHomeChannel.roomInfo.root).toBeVisible();
});
await test.step('expect to update room SLA policy', async () => {
await expect(poHomeChannel.roomInfo.getInfoByLabel('SLA Policy')).not.toBeVisible();
await poHomeChannel.roomInfo.btnEdit.click();
await poHomeChannel.editRoomInfo.selectSLA('Urgent');
await poHomeChannel.editRoomInfo.btnSave.click();
});
await test.step('expect SLA to have been updated in the room info and queue order to be correct', async () => {
await expect(poHomeChannel.roomInfo.getInfoByLabel('SLA Policy')).toHaveText('Urgent');
await expect(poHomeChannel.sidebar.getSidebarListItem(visitorB.name)).toHaveAttribute('data-index', '1');
await expect(poHomeChannel.sidebar.getSidebarListItem(visitorA.name)).toHaveAttribute('data-index', '2');
});
});
await test.step('expect to change room SLA policy to "Very Urgent"', async () => {
await test.step('expect to open room and room info to be visible', async () => {
await poHomeChannel.sidebar.getSidebarItemByName(visitorC.name).click();
await expect(poHomeChannel.roomInfo.root).toBeVisible();
});
await test.step('expect to update room SLA policy', async () => {
await expect(poHomeChannel.roomInfo.getInfoByLabel('SLA Policy')).not.toBeVisible();
await poHomeChannel.roomInfo.btnEdit.click();
await poHomeChannel.editRoomInfo.selectSLA('Very Urgent');
await poHomeChannel.editRoomInfo.btnSave.click();
});
await test.step('expect SLA to have been updated in the room info and queue order to be correct', async () => {
await expect(poHomeChannel.roomInfo.getInfoByLabel('SLA Policy')).toHaveText('Very Urgent');
await expect(poHomeChannel.sidebar.getSidebarListItem(visitorC.name)).toHaveAttribute('data-index', '1');
await expect(poHomeChannel.sidebar.getSidebarListItem(visitorB.name)).toHaveAttribute('data-index', '2');
await expect(poHomeChannel.sidebar.getSidebarListItem(visitorA.name)).toHaveAttribute('data-index', '3');
});
});
});
});

View File

@@ -0,0 +1,135 @@
import { faker } from '@faker-js/faker';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelSlaPolicies } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
const ERROR = {
nameRequired: 'Name required',
estimatedWaitTimeRequired: 'Estimated wait time (time in minutes) required',
};
const INITIAL_SLA = {
name: faker.person.firstName(),
description: faker.lorem.sentence(),
estimatedWaitTime: faker.string.numeric({ length: 1, exclude: '0' }),
};
const EDITED_SLA = {
name: faker.person.firstName(),
description: faker.lorem.sentence(),
estimatedWaitTime: faker.string.numeric({ length: 1, exclude: '0' }),
};
test.skip(!IS_EE, 'Omnichannel SLA Policies > Enterprise Only');
test.use({ storageState: Users.user1.state });
test.describe('Omnichannel SLA Policies', () => {
let poOmnichannelSlaPolicies: OmnichannelSlaPolicies;
test.beforeAll(async ({ api }) => {
await api.post('/livechat/users/agent', { username: 'user1' });
await api.post('/livechat/users/manager', { username: 'user1' });
});
test.beforeEach(async ({ page }) => {
poOmnichannelSlaPolicies = new OmnichannelSlaPolicies(page);
await poOmnichannelSlaPolicies.goto();
});
test.afterAll(async ({ api }) => {
await api.delete('/livechat/users/agent/user1');
await api.delete('/livechat/users/manager/user1');
});
test('Manage SLAs', async () => {
await test.step('Add new SLA', async () => {
await poOmnichannelSlaPolicies.createNew();
await test.step('should not submit form with empty required fields: name and estimated wait time', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.btnSave.click();
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.errorMessage(ERROR.nameRequired)).toBeVisible();
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.errorMessage(ERROR.estimatedWaitTimeRequired)).toBeVisible();
});
await test.step('input a valid name', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.inputName.fill(INITIAL_SLA.name);
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.errorMessage(ERROR.nameRequired)).not.toBeVisible();
});
await test.step('input a valid description', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.inputDescription.fill(INITIAL_SLA.description);
});
await test.step('only allow numbers on estimated wait time field', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.inputEstimatedWaitTime.pressSequentially('a');
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.inputEstimatedWaitTime).toHaveValue('0');
});
await test.step('not allow 0 on estimated wait time field', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.inputEstimatedWaitTime.fill('0');
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.errorMessage(ERROR.estimatedWaitTimeRequired)).toBeVisible();
});
await test.step('input a valid estimated wait time', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.inputEstimatedWaitTime.fill(INITIAL_SLA.estimatedWaitTime);
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.errorMessage(ERROR.estimatedWaitTimeRequired)).not.toBeVisible();
});
await test.step('save sla', async () => {
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.btnSave).toBeEnabled();
await poOmnichannelSlaPolicies.manageSlaPolicy.btnSave.click();
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.inputName).not.toBeVisible();
await expect(poOmnichannelSlaPolicies.table.findRowByName(INITIAL_SLA.name)).toBeVisible();
});
});
await test.step('Search SLA', async () => {
await poOmnichannelSlaPolicies.inputSearch.fill('random_text_that_should_have_no_match');
await expect(poOmnichannelSlaPolicies.table.findRowByName(INITIAL_SLA.name)).not.toBeVisible();
await expect(poOmnichannelSlaPolicies.emptyState).toBeVisible();
await poOmnichannelSlaPolicies.inputSearch.fill(INITIAL_SLA.name);
await expect(poOmnichannelSlaPolicies.table.findRowByName(INITIAL_SLA.name)).toBeVisible();
await expect(poOmnichannelSlaPolicies.emptyState).not.toBeVisible();
await poOmnichannelSlaPolicies.inputSearch.fill('');
});
await test.step('Edit SLA', async () => {
await poOmnichannelSlaPolicies.table.findRowByName(INITIAL_SLA.name).click();
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.inputName).toHaveValue(INITIAL_SLA.name);
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.inputDescription).toHaveValue(INITIAL_SLA.description);
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.inputEstimatedWaitTime).toHaveValue(INITIAL_SLA.estimatedWaitTime);
await test.step('edit name', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.inputName.fill(EDITED_SLA.name);
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.errorMessage(ERROR.nameRequired)).not.toBeVisible();
});
await test.step('edit description', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.inputDescription.fill(EDITED_SLA.description);
});
await test.step('edit estimated wait time', async () => {
await poOmnichannelSlaPolicies.manageSlaPolicy.inputEstimatedWaitTime.fill(EDITED_SLA.estimatedWaitTime);
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.errorMessage(ERROR.estimatedWaitTimeRequired)).not.toBeVisible();
});
await test.step('save sla', async () => {
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.btnSave).toBeEnabled();
await poOmnichannelSlaPolicies.manageSlaPolicy.btnSave.click();
await expect(poOmnichannelSlaPolicies.manageSlaPolicy.inputName).not.toBeVisible();
await expect(poOmnichannelSlaPolicies.table.findRowByName(EDITED_SLA.name)).toBeVisible();
});
});
await test.step('Remove SLA', async () => {
await poOmnichannelSlaPolicies.removeSLA(EDITED_SLA.name);
await expect(poOmnichannelSlaPolicies.table.findRowByName(EDITED_SLA.name)).not.toBeVisible();
});
});
});

View File

@@ -0,0 +1,131 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelTags } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent } from '../utils/omnichannel/agents';
import { createDepartment } from '../utils/omnichannel/departments';
import { createTag } from '../utils/omnichannel/tags';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe('OC - Manage Tags', () => {
test.skip(!IS_EE, 'OC - Manage Tags > Enterprise Edition Only');
let poOmnichannelTags: OmnichannelTags;
let department: Awaited<ReturnType<typeof createDepartment>>;
let department2: Awaited<ReturnType<typeof createDepartment>>;
let agent: Awaited<ReturnType<typeof createAgent>>;
test.beforeAll(async ({ api }) => {
department = await createDepartment(api);
department2 = await createDepartment(api);
});
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user2');
});
test.beforeAll(async ({ api }) => {
await setSettingValueById(api, 'Omnichannel_enable_department_removal', true);
});
test.afterAll(async ({ api }) => {
await department.delete();
await department2.delete();
await agent.delete();
await setSettingValueById(api, 'Omnichannel_enable_department_removal', false);
});
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannelTags = new OmnichannelTags(page);
});
test('OC - Manage Tags - Create Tag', async () => {
const tagName = faker.string.uuid();
await poOmnichannelTags.goto();
await test.step('expect correct form default state', async () => {
await poOmnichannelTags.createNew();
await expect(poOmnichannelTags.editTag.root).toBeVisible();
await expect(poOmnichannelTags.editTag.btnSave).toBeEnabled();
await expect(poOmnichannelTags.editTag.btnCancel).toBeEnabled();
await poOmnichannelTags.editTag.btnCancel.click();
await expect(poOmnichannelTags.editTag.root).not.toBeVisible();
});
await test.step('expect to create new tag', async () => {
await poOmnichannelTags.createNew();
await poOmnichannelTags.editTag.inputName.fill(tagName);
await poOmnichannelTags.editTag.selectDepartment(department.data.name);
await poOmnichannelTags.editTag.btnSave.click();
await expect(poOmnichannelTags.editTag.root).not.toBeVisible();
await test.step('expect tag to have been created', async () => {
await poOmnichannelTags.search(tagName);
await expect(poOmnichannelTags.table.findRowByName(tagName)).toBeVisible();
});
});
await test.step('should be able to delete tag', async () => {
await poOmnichannelTags.deleteTag(tagName);
await poOmnichannelTags.waitForEmptyState();
});
});
test('OC - Manage Tags - Edit tag departments', async ({ api, page }) => {
const tag = await test.step('expect to create new tag', async () => {
const { data: tag } = await createTag(api, {
name: faker.string.uuid(),
departments: [department.data._id],
});
return tag;
});
await poOmnichannelTags.goto();
await test.step('expect to add tag departments', async () => {
await poOmnichannelTags.search(tag.name);
await poOmnichannelTags.table.findRowByName(tag.name).click();
await expect(poOmnichannelTags.editTag.root).toBeVisible();
await poOmnichannelTags.editTag.selectDepartment(department2.data.name);
await expect(poOmnichannelTags.editTag.findSelectedDepartment(department2.data.name)).toBeVisible();
await poOmnichannelTags.editTag.save();
});
await test.step('expect department to be in the chosen departments list', async () => {
await poOmnichannelTags.search(tag.name);
await poOmnichannelTags.table.findRowByName(tag.name).click();
await expect(poOmnichannelTags.editTag.root).toBeVisible();
await expect(poOmnichannelTags.editTag.inputDepartments).toBeVisible();
await poOmnichannelTags.editTag.close();
});
await test.step('expect to remove tag departments', async () => {
await poOmnichannelTags.search(tag.name);
await poOmnichannelTags.table.findRowByName(tag.name).click();
await expect(poOmnichannelTags.editTag.root).toBeVisible();
await poOmnichannelTags.editTag.selectDepartment(department2.data.name);
await expect(poOmnichannelTags.editTag.findSelectedDepartment(department2.data.name)).not.toBeVisible();
await poOmnichannelTags.editTag.save();
});
await test.step('expect department to not be in the chosen departments list', async () => {
await poOmnichannelTags.search(tag.name);
await poOmnichannelTags.table.findRowByName(tag.name).click();
await expect(poOmnichannelTags.editTag.root).toBeVisible();
await expect(page.getByRole('option', { name: department2.data.name })).toBeHidden();
});
await test.step('expect to delete tag', async () => {
await poOmnichannelTags.deleteTag(tag.name);
await poOmnichannelTags.waitForEmptyState();
});
});
});

View File

@@ -0,0 +1,99 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { expectPollUserStatus } from '../utils/expectPollUserStatus';
import { test, expect } from '../utils/test';
test.describe('omnichannel-takeChat', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeChannel: HomeOmnichannel };
const sendLivechatMessage = async () => {
await poLiveChat.openLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.fill('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
};
test.beforeAll(async ({ api, browser }) => {
await Promise.all([
await api.post('/livechat/users/agent', { username: 'user1' }).then((res) => expect(res.status()).toBe(200)),
await api.post('/settings/Livechat_Routing_Method', { value: 'Manual_Selection' }).then((res) => expect(res.status()).toBe(200)),
await api.post('/settings/Livechat_enabled_when_agent_idle', { value: false }).then((res) => expect(res.status()).toBe(200)),
]);
const { page } = await createAuxContext(browser, Users.user1);
agent = { page, poHomeChannel: new HomeOmnichannel(page) };
});
test.afterAll(async ({ api }) => {
await agent.poHomeChannel.navbar.switchOmnichannelStatus('online');
await agent.poHomeChannel.navbar.changeUserStatus('online');
await agent.page.close();
await Promise.all([
await api.delete('/livechat/users/agent/user1'),
await api.post('/settings/Livechat_Routing_Method', { value: 'Auto_Selection' }),
await api.post('/settings/Livechat_enabled_when_agent_idle', { value: true }),
]);
});
test.beforeEach('start a new livechat chat', async ({ page, api }) => {
await agent.poHomeChannel.navbar.changeUserStatus('online');
await expectPollUserStatus(api, 'user1', 'online');
newVisitor = createFakeVisitor();
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
});
test('When agent is online should take the chat', async () => {
await sendLivechatMessage();
await agent.poHomeChannel.sidebar.getSidebarItemByName(newVisitor.name).click();
await expect(agent.poHomeChannel.content.btnTakeChat).toBeVisible();
await agent.poHomeChannel.content.btnTakeChat.click();
await agent.poHomeChannel.navbar.openChat(newVisitor.name);
await expect(agent.poHomeChannel.content.btnTakeChat).not.toBeVisible();
await expect(agent.poHomeChannel.composer.inputMessage).toBeVisible();
});
test('When agent is offline should not take the chat', async ({ api }) => {
await agent.poHomeChannel.navbar.changeUserStatus('offline');
await expectPollUserStatus(api, 'user1', 'offline');
await sendLivechatMessage();
await expect(poLiveChat.alertMessage('Error starting a new conversation: Sorry, no online agents [no-agent-online]')).toBeVisible();
});
test('When a new livechat conversation is selected and the agent becomes offline or unavailable, they should not be able to take the chat', async ({
api,
}) => {
await sendLivechatMessage();
await agent.poHomeChannel.sidebar.getSidebarItemByName(newVisitor.name).click();
await agent.poHomeChannel.navbar.changeUserStatus('offline');
await expectPollUserStatus(api, 'user1', 'offline');
await expect(agent.poHomeChannel.content.btnTakeChat).toBeDisabled();
await agent.poHomeChannel.navbar.changeUserStatus('online');
await expectPollUserStatus(api, 'user1', 'online');
await agent.poHomeChannel.navbar.switchOmnichannelStatus('offline');
await expect(agent.poHomeChannel.content.btnTakeChat).toBeDisabled();
});
});

View File

@@ -0,0 +1,98 @@
import type { Page } from '@playwright/test';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { createAgent } from '../utils/omnichannel/agents';
import { createManager } from '../utils/omnichannel/managers';
import { createConversation } from '../utils/omnichannel/rooms';
import { test, expect } from '../utils/test';
test.describe('OC - Chat transfers [Agent role]', () => {
let sessions: { page: Page; poHomeOmnichannel: HomeOmnichannel }[];
let agents: Awaited<ReturnType<typeof createAgent>>[];
let managers: Awaited<ReturnType<typeof createManager>>[];
let conversations: Awaited<ReturnType<typeof createConversation>>[];
// Create agents and managers
test.beforeAll(async ({ api }) => {
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]);
managers = await Promise.all([createManager(api, 'user1')]);
});
// Livechat when agent idle
test.beforeAll(async ({ api }) => {
await api.post('/settings/Livechat_enabled_when_agent_idle', { value: false }).then((res) => expect(res.status()).toBe(200));
});
// Create agent sessions
test.beforeAll(async ({ browser }) => {
sessions = await Promise.all([
createAuxContext(browser, Users.user1).then(({ page }) => ({ page, poHomeOmnichannel: new HomeOmnichannel(page) })),
createAuxContext(browser, Users.user2).then(({ page }) => ({ page, poHomeOmnichannel: new HomeOmnichannel(page) })),
]);
});
// Delete all data
test.afterAll(async ({ api }) => {
await Promise.all([
...conversations.map((conversation) => conversation.delete()),
...agents.map((agent) => agent.delete()),
...managers.map((manager) => manager.delete()),
api.post('/settings/Livechat_enabled_when_agent_idle', { value: true }).then((res) => expect(res.status()).toBe(200)),
]);
});
// Make "user-1" online & "user-2" offline so that chat can be automatically routed to "user-1"
test.beforeAll(async () => {
const [agentA, agentB] = sessions;
await agentA.poHomeOmnichannel.navbar.changeUserStatus('online');
await agentB.poHomeOmnichannel.navbar.changeUserStatus('offline');
});
// Close sessions
test.afterAll(async () => {
await Promise.all(sessions.map(({ page }) => page.close()));
});
// Start a new chat for each test
test.beforeAll(async ({ api }) => {
conversations = [await createConversation(api)];
});
test('OC - Chat transfers [Agent role] - Transfer omnichannel chat to another agent', async () => {
const [agentA, agentB] = sessions;
const [{ visitor }] = conversations.map(({ data }) => data);
await test.step('expect to have 1 omnichannel assigned to agent 1', async () => {
await agentA.poHomeOmnichannel.sidebar.getSidebarItemByName(visitor.name).click();
});
await test.step('expect to not be able to transfer chat to "user-2" when that user is offline', async () => {
await agentB.poHomeOmnichannel.navbar.changeUserStatus('offline');
await agentA.poHomeOmnichannel.quickActionsRoomToolbar.forwardChat();
await agentA.poHomeOmnichannel.content.forwardChatModal.inputForwardUser.click();
await agentA.poHomeOmnichannel.content.forwardChatModal.inputForwardUser.type('user2');
await expect(agentA.page.locator('text=Empty')).toBeVisible();
await agentA.poHomeOmnichannel.goto();
});
await test.step('expect to be able to transfer an omnichannel to conversation to agent 2 as agent 1 when agent 2 is online', async () => {
await agentB.poHomeOmnichannel.navbar.changeUserStatus('online');
await agentA.poHomeOmnichannel.sidebar.getSidebarItemByName(visitor.name).click();
await agentA.poHomeOmnichannel.quickActionsRoomToolbar.forwardChat();
await agentA.poHomeOmnichannel.content.forwardChatModal.selectUser('user2');
await agentA.poHomeOmnichannel.content.forwardChatModal.inputComment.type('any_comment');
await agentA.poHomeOmnichannel.content.forwardChatModal.btnForward.click();
await agentA.poHomeOmnichannel.toastMessage.waitForDisplay();
});
await test.step('expect to have 1 omnichannel assigned to agent 2', async () => {
await agentB.poHomeOmnichannel.sidebar.getSidebarItemByName(visitor.name).click();
});
});
});

View File

@@ -0,0 +1,133 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe('OC - Livechat New Chat Triggers - After Registration', () => {
let triggersName: string;
let triggerMessage: string;
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeEach(async ({ api, browser, page }) => {
newVisitor = createFakeVisitor();
triggersName = faker.string.uuid();
triggerMessage = 'This is a trigger message after guest registration';
await api.post('/livechat/triggers', {
name: triggersName,
description: 'Creating a fresh trigger',
enabled: true,
runOnce: false,
conditions: [
{
name: 'after-guest-registration',
value: '',
},
],
actions: [
{
name: 'send-message',
params: {
name: '',
msg: triggerMessage,
sender: 'queue',
},
},
],
});
await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
]);
const { page: agentPage } = await createAuxContext(browser, Users.user1);
agent = { page: agentPage, poHomeOmnichannel: new HomeOmnichannel(agentPage) };
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterEach(async ({ api }) => {
const ids = (await (await api.get('/livechat/triggers')).json()).triggers.map(
(trigger: { _id: string }) => trigger._id,
) as unknown as string[];
await Promise.all(ids.map((id) => api.delete(`/livechat/triggers/${id}`)));
await Promise.all([api.delete('/livechat/users/agent/user1'), api.delete('/livechat/users/manager/user1')]);
await agent.page.close();
});
test.afterAll(async ({ api }) => {
await api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: false });
});
test.describe('OC - Livechat New Chat Triggers - After Registration', async () => {
test('expect trigger message after registration', async () => {
await poLiveChat.goto();
await poLiveChat.sendMessageAndCloseChat(newVisitor);
await poLiveChat.startNewChat();
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
});
});
test.describe('OC - Livechat Triggers - After Registration - Reload', async () => {
test('expect trigger message after registration', async () => {
await test.step('expect trigger message after registration to be visible', async () => {
await poLiveChat.goto();
await poLiveChat.openAnyLiveChat();
await poLiveChat.sendMessage(newVisitor, false);
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
});
await test.step('expect trigger message after registration to be visible after reload', async () => {
await poLiveChat.page.reload();
await poLiveChat.openAnyLiveChat();
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
});
await test.step('expect to close room and reload', async () => {
await poLiveChat.onlineAgentMessage.type('message_after_trigger');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('message_after_trigger')).toBeVisible();
await poLiveChat.closeChat();
await expect(poLiveChat.btnNewChat).toBeVisible();
await poLiveChat.startNewChat();
await poLiveChat.page.reload();
});
await test.step('expect trigger message after registration to be visible after reload on new chat', async () => {
await poLiveChat.openAnyLiveChat();
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
});
});
});
test.describe('OC - Livechat New Chat Triggers - After Registration, clear Local storage', async () => {
test.beforeAll(async ({ api }) => {
await api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: true });
});
test('expect trigger message after registration not be visible after local storage clear', async () => {
await poLiveChat.goto();
await poLiveChat.sendMessageAndCloseChat(newVisitor);
await expect(poLiveChat.btnNewChat).toBeVisible();
await expect(poLiveChat.txtChatMessage(triggerMessage)).not.toBeVisible();
await poLiveChat.startNewChat();
await poLiveChat.sendMessage(newVisitor, false);
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
});
});
});

View File

@@ -0,0 +1,98 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe('OC - Livechat Triggers - Open by Visitor', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
const requests = await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
api.post(
'/livechat/triggers',
{
name: 'open',
description: '',
enabled: true,
runOnce: false,
conditions: [
{
name: 'chat-opened-by-visitor',
value: '',
},
],
actions: [
{
name: 'send-message',
params: {
name: '',
msg: 'This is a trigger message open by visitor',
sender: 'queue',
},
},
],
},
),
]);
requests.every((e) => expect(e.status()).toBe(200));
const { page } = await createAuxContext(browser, Users.user1, '/');
agent = { page, poHomeOmnichannel: new HomeOmnichannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
});
test.afterAll(async ({ api }) => {
const ids = (await (await api.get('/livechat/triggers')).json()).triggers.map(
(trigger: { _id: string }) => trigger._id,
) as unknown as string[];
await Promise.all(ids.map((id) => api.delete(`/livechat/triggers/${id}`)));
await Promise.all([
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/manager/user1'),
api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: false }),
]);
await agent.page.close();
});
test('OC - Livechat Triggers - after the visitor opens the chat the trigger message should not be visible neither after a page reload', async () => {
await poLiveChat.openLiveChat();
await expect(poLiveChat.txtChatMessage('This is a trigger message open by visitor')).toBeVisible();
await poLiveChat.btnChatNow.click();
await poLiveChat.sendMessage(newVisitor, false);
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_visitor')).toBeVisible();
await poLiveChat.page.reload();
// if the request took too long, the loadMessage cleans triggers, but if the fetch is fast enough, the trigger message will be visible
await poLiveChat.page.waitForRequest(/livechat\/messages.history/);
await poLiveChat.openLiveChat();
await expect(poLiveChat.txtChatMessage('This is a trigger message open by visitor')).not.toBeVisible();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_visitor')).toBeVisible();
});
});

View File

@@ -0,0 +1,128 @@
import { IS_EE } from '../config/constants';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChatEmbedded } from '../page-objects/omnichannel';
import { createAgent } from '../utils/omnichannel/agents';
import { addAgentToDepartment, createDepartment } from '../utils/omnichannel/departments';
import { test, expect } from '../utils/test';
declare const window: Window & {
RocketChat: { livechat: { setDepartment: (dep: string) => void; maximizeWidget: () => void } };
};
test.use({ storageState: Users.admin.state });
test.describe('OC - Livechat Triggers - SetDepartment', () => {
test.skip(!IS_EE, 'Enterprise Only');
let poLiveChat: OmnichannelLiveChatEmbedded;
let departments: Awaited<ReturnType<typeof createDepartment>>[];
let departmentA: Awaited<ReturnType<typeof createDepartment>>['data'];
let departmentB: Awaited<ReturnType<typeof createDepartment>>['data'];
let agents: Awaited<ReturnType<typeof createDepartment>>[];
let agent1: Awaited<ReturnType<typeof createAgent>>['data'];
let agent2: Awaited<ReturnType<typeof createAgent>>['data'];
let poHomeOmnichannelAgent1: HomeOmnichannel;
let poHomeOmnichannelAgent2: HomeOmnichannel;
test.beforeAll(async ({ api }) => {
// Assign agents & departments
agents = await Promise.all([createAgent(api, 'user1'), createAgent(api, 'user2')]);
[agent1, agent2] = agents.map(({ data }) => data);
departments = await Promise.all([
createDepartment(api, { showOnRegistration: true }),
createDepartment(api, { showOnRegistration: true }),
]);
[departmentA, departmentB] = departments.map(({ data }) => data);
await Promise.all([
addAgentToDepartment(api, { department: departmentA, agentId: agent1._id }),
addAgentToDepartment(api, { department: departmentB, agentId: agent2._id }),
api.post(
'/livechat/triggers',
{
name: 'open',
description: '',
enabled: true,
runOnce: false,
conditions: [
{
name: 'chat-opened-by-visitor',
value: '',
},
],
actions: [
{
name: 'send-message',
params: {
name: '',
msg: 'This is a trigger message open by visitor',
sender: 'queue',
},
},
],
},
),
]);
});
test.beforeEach(async ({ browser, page }) => {
const { page: agent1Page } = await createAuxContext(browser, Users.user1, '/', true);
poHomeOmnichannelAgent1 = new HomeOmnichannel(agent1Page);
const { page: agent2Page } = await createAuxContext(browser, Users.user2, '/', true);
poHomeOmnichannelAgent2 = new HomeOmnichannel(agent2Page);
poLiveChat = new OmnichannelLiveChatEmbedded(page);
});
test.afterEach(async ({ page }) => {
await poHomeOmnichannelAgent1.page.close();
await poHomeOmnichannelAgent2.page.close();
await poLiveChat.page.close();
await page.close();
});
test.afterAll(async ({ api }) => {
const ids = (await (await api.get('/livechat/triggers')).json()).triggers.map(
(trigger: { _id: string }) => trigger._id,
) as unknown as string[];
await Promise.all(ids.map((id) => api.delete(`/livechat/triggers/${id}`)));
expect((await api.post('/settings/Omnichannel_enable_department_removal', { value: true })).status()).toBe(200);
await Promise.all([...agents.map((agent) => agent.delete())]);
await Promise.all([...departments.map((department) => department.delete())]);
expect((await api.post('/settings/Omnichannel_enable_department_removal', { value: false })).status()).toBe(200);
await api.post('/settings/Livechat_registration_form', { value: true });
});
test('OC - Livechat Triggers - setDepartment should affect agent.next call', async () => {
await poLiveChat.goto();
const depId = departmentB._id;
await poLiveChat.page.evaluate((depId) => window.RocketChat.livechat.setDepartment(depId), depId);
await poLiveChat.openLiveChat();
await expect(poLiveChat.txtChatMessage('This is a trigger message open by visitor')).toBeVisible();
await expect(poLiveChat.headerTitle).toContainText(agent2.username);
});
test('OC - Livechat Triggers - setDepartment should affect agent.next call - Register Form Disabled', async ({ api }) => {
await api.post('/settings/Livechat_registration_form', { value: false });
await poLiveChat.goto();
const depId = departmentB._id;
await poLiveChat.page.evaluate((depId) => window.RocketChat.livechat.setDepartment(depId), depId);
await poLiveChat.openLiveChat();
await expect(poLiveChat.txtChatMessage('This is a trigger message open by visitor')).toBeVisible();
await expect(poLiveChat.headerTitle).toContainText(agent2.username);
});
});

View File

@@ -0,0 +1,97 @@
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { HomeOmnichannel } from '../page-objects';
import { OmnichannelLiveChat } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe('OC - Livechat Triggers - Time on site', () => {
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeOmnichannel: HomeOmnichannel };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
const requests = await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
api.post(
'/livechat/triggers',
{
enabled: true,
runOnce: false,
conditions: [{ name: 'time-on-site', value: '1' }],
actions: [{ name: 'send-message', params: { name: '', msg: 'This is a trigger message time on site', sender: 'queue' } }],
name: 'test',
description: 'test',
},
),
]);
requests.every((e) => expect(e.status()).toBe(200));
const { page } = await createAuxContext(browser, Users.user1, '/');
agent = { page, poHomeOmnichannel: new HomeOmnichannel(page) };
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
await poLiveChat.goto();
});
test.afterAll(async ({ api }) => {
const ids = (await (await api.get('/livechat/triggers')).json()).triggers.map(
(trigger: { _id: string }) => trigger._id,
) as unknown as string[];
await Promise.all(ids.map((id) => api.delete(`/livechat/triggers/${id}`)));
await Promise.all([
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/manager/user1'),
api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: false }),
]);
await agent.page.close();
});
test('expect to receive trigger message after 1 second', async () => {
await expect(poLiveChat.page.locator('role=button[name="Close"]')).toBeVisible();
await expect(poLiveChat.page.locator('role=main')).toContainText('This is a trigger message time on site');
await expect(poLiveChat.page.locator('role=button[name="Start chat"]')).toBeVisible();
await expect(poLiveChat.page.locator('role=button[name="Messages"]')).toBeVisible();
});
test('OC - Livechat Triggers - after the visitor opens the chat the trigger time-on-site should not be triggered after reload', async () => {
await expect(poLiveChat.page.locator('role=button[name="Close"]')).toBeVisible();
await expect(poLiveChat.page.locator('role=main')).toContainText('This is a trigger message time on site');
await expect(poLiveChat.page.locator('role=button[name="Start chat"]')).toBeVisible();
await expect(poLiveChat.page.locator('role=button[name="Messages"]')).toBeVisible();
await poLiveChat.btnOpenOnlineLiveChat('Start chat').click();
await poLiveChat.btnOpenOnlineLiveChat('Chat now').click();
await poLiveChat.sendMessage(newVisitor, false);
await test.step('expect to not have any trigger message after registration', async () => {
await expect(poLiveChat.txtChatMessage('This is a trigger message time on site')).not.toBeVisible();
});
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_visitor');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.page.locator('role=main')).toContainText('Chat started');
await poLiveChat.page.reload();
await poLiveChat.btnOpenOnlineLiveChat('Rocket.Chat').click();
await expect(poLiveChat.page.locator('role=main')).toContainText('Chat started');
await poLiveChat.page.waitForTimeout(1000);
await expect(poLiveChat.txtChatMessage('This is a trigger message time on site')).not.toBeVisible();
});
});

View File

@@ -0,0 +1,159 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { createFakeVisitor } from '../../mocks/data';
import { createAuxContext } from '../fixtures/createAuxContext';
import { Users } from '../fixtures/userStates';
import { OmnichannelLiveChat, OmnichannelTriggers } from '../page-objects/omnichannel';
import { test, expect } from '../utils/test';
test.describe.serial('OC - Livechat Triggers', () => {
let triggersName: string;
let triggerMessage: string;
let poLiveChat: OmnichannelLiveChat;
let newVisitor: { email: string; name: string };
let agent: { page: Page; poHomeOmnichannelTriggers: OmnichannelTriggers };
test.beforeAll(async ({ api, browser }) => {
newVisitor = createFakeVisitor();
triggersName = faker.string.uuid();
triggerMessage = 'This is a trigger message';
const requests = await Promise.all([
api.post('/livechat/users/agent', { username: 'user1' }),
api.post('/livechat/users/manager', { username: 'user1' }),
api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: true }),
]);
requests.every((e) => expect(e.status()).toBe(200));
const { page } = await createAuxContext(browser, Users.user1, '/omnichannel/triggers');
agent = { page, poHomeOmnichannelTriggers: new OmnichannelTriggers(page) };
await page.emulateMedia({ reducedMotion: 'reduce' });
});
test.beforeEach(async ({ page, api }) => {
poLiveChat = new OmnichannelLiveChat(page, api);
});
test.afterAll(async ({ api }) => {
const ids = (await (await api.get('/livechat/triggers')).json()).triggers.map(
(trigger: { _id: string }) => trigger._id,
) as unknown as string[];
await Promise.all(ids.map((id) => api.delete(`/livechat/triggers/${id}`)));
await Promise.all([
api.delete('/livechat/users/agent/user1'),
api.delete('/livechat/users/manager/user1'),
api.post('/settings/Livechat_clear_local_storage_when_chat_ended', { value: false }),
]);
await agent.page.close();
});
test('OC - Livechat Triggers - Baseline', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
await test.step('expect to register visitor', async () => {
await expect(poLiveChat.btnChatNow).not.toBeVisible();
await poLiveChat.sendMessage(newVisitor, false);
});
await test.step('expect send a message as a visitor', async () => {
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_user')).toBeVisible();
});
await test.step('expect to finish this chat', async () => {
await poLiveChat.closeChat();
await expect(poLiveChat.txtHeaderTitle).toBeVisible();
});
});
test('OC - Livechat Triggers - Create and edit trigger', async () => {
triggerMessage = 'This is a trigger message time on site';
await test.step('expect create new trigger', async () => {
await agent.poHomeOmnichannelTriggers.createTrigger(triggersName, triggerMessage, 'Visitor time on site', 5);
});
triggerMessage = 'This is a trigger message chat opened by visitor';
await test.step('expect update trigger', async () => {
await agent.poHomeOmnichannelTriggers.table.findRowByName(triggersName).click();
await agent.poHomeOmnichannelTriggers.updateTrigger(`edited-${triggersName}`, triggerMessage);
});
});
test('OC - Livechat Triggers - Condition: chat opened by visitor', async () => {
await test.step('expect to start conversation', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
});
await test.step('expect trigger message before registration', async () => {
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
});
await test.step('expect to register visitor', async () => {
await poLiveChat.btnChatNow.click();
await poLiveChat.sendMessage(newVisitor, false);
});
await test.step('expect trigger message after registration', async () => {
await expect(poLiveChat.txtChatMessage(triggerMessage)).not.toBeVisible();
});
await test.step('expect send a message as a visitor', async () => {
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_user')).toBeVisible();
});
await test.step('expect to finish this chat', async () => {
await poLiveChat.closeChat();
await expect(poLiveChat.txtHeaderTitle).toBeVisible();
});
});
test('OC - Livechat Triggers - Condition: after guest registration', async () => {
triggerMessage = 'This is a trigger message after guest registration';
await test.step('expect update trigger to after guest registration', async () => {
await agent.poHomeOmnichannelTriggers.table.findRowByName(`edited-${triggersName}`).click();
await agent.poHomeOmnichannelTriggers.updateTrigger(`re-edited-${triggersName}`, triggerMessage, 'After guest registration');
});
await test.step('expect to start conversation', async () => {
await poLiveChat.goto();
await poLiveChat.openLiveChat();
});
await test.step('expect not to have trigger message before registration', async () => {
await expect(poLiveChat.txtChatMessage(triggerMessage)).not.toBeVisible();
await expect(poLiveChat.btnChatNow).not.toBeVisible();
});
await test.step('expect to register visitor', async () => {
await expect(poLiveChat.inputName).toBeVisible();
await poLiveChat.sendMessage(newVisitor, false);
});
await test.step('expect trigger message after registration', async () => {
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
});
await test.step('expect send a message as a visitor', async () => {
await poLiveChat.onlineAgentMessage.type('this_a_test_message_from_user');
await poLiveChat.btnSendMessageToOnlineAgent.click();
await expect(poLiveChat.txtChatMessage('this_a_test_message_from_user')).toBeVisible();
await expect(poLiveChat.txtChatMessage(triggerMessage)).toBeVisible();
});
await test.step('expect to finish this chat', async () => {
await poLiveChat.closeChat();
await expect(poLiveChat.txtHeaderTitle).toBeVisible();
});
});
test('OC - Livechat Triggers - Delete trigger', async () => {
await agent.poHomeOmnichannelTriggers.removeTrigger(`re-edited-${triggersName}`);
});
});

View File

@@ -0,0 +1,214 @@
import { faker } from '@faker-js/faker';
import type { Page } from '@playwright/test';
import { IS_EE } from '../config/constants';
import { Users } from '../fixtures/userStates';
import { OmnichannelUnits } from '../page-objects/omnichannel';
import { setSettingValueById } from '../utils';
import { createAgent } from '../utils/omnichannel/agents';
import { createDepartment } from '../utils/omnichannel/departments';
import { createMonitor } from '../utils/omnichannel/monitors';
import { createOrUpdateUnit } from '../utils/omnichannel/units';
import { test, expect } from '../utils/test';
test.use({ storageState: Users.admin.state });
test.describe('OC - Manage Units', () => {
test.skip(!IS_EE, 'OC - Manage Units > Enterprise Edition Only');
let poOmnichannelUnits: OmnichannelUnits;
let department: Awaited<ReturnType<typeof createDepartment>>;
let department2: Awaited<ReturnType<typeof createDepartment>>;
let agent: Awaited<ReturnType<typeof createAgent>>;
let monitor: Awaited<ReturnType<typeof createMonitor>>;
let monitor2: Awaited<ReturnType<typeof createMonitor>>;
test.beforeAll(async ({ api }) => {
department = await createDepartment(api);
department2 = await createDepartment(api);
});
test.beforeAll(async ({ api }) => {
agent = await createAgent(api, 'user2');
});
test.beforeAll(async ({ api }) => {
monitor = await createMonitor(api, 'user2');
monitor2 = await createMonitor(api, 'user3');
});
test.beforeAll(async ({ api }) => {
await setSettingValueById(api, 'Omnichannel_enable_department_removal', true);
});
test.afterAll(async ({ api }) => {
await department.delete();
await department2.delete();
await monitor.delete();
await monitor2.delete();
await agent.delete();
await setSettingValueById(api, 'Omnichannel_enable_department_removal', false);
});
test.beforeEach(async ({ page }: { page: Page }) => {
poOmnichannelUnits = new OmnichannelUnits(page);
await poOmnichannelUnits.goto();
});
test('OC - Manage Units - Create Unit', async () => {
const unitName = faker.string.uuid();
await test.step('expect correct form default state', async () => {
await poOmnichannelUnits.createNew();
await expect(poOmnichannelUnits.manageUnit.root).toBeVisible();
await expect(poOmnichannelUnits.manageUnit.btnSave).toBeEnabled();
await expect(poOmnichannelUnits.manageUnit.btnCancel).toBeEnabled();
await poOmnichannelUnits.manageUnit.btnCancel.click();
await expect(poOmnichannelUnits.manageUnit.root).not.toBeVisible();
});
await test.step('expect to create new unit', async () => {
await poOmnichannelUnits.createNew();
await poOmnichannelUnits.manageUnit.inputName.fill(unitName);
await poOmnichannelUnits.manageUnit.selectVisibility('public');
await poOmnichannelUnits.manageUnit.selectDepartment(department.data.name);
await poOmnichannelUnits.manageUnit.selectMonitor('user2');
await expect(poOmnichannelUnits.manageUnit.findMonitorChipOption('user2')).toBeVisible();
await poOmnichannelUnits.manageUnit.btnSave.click();
await expect(poOmnichannelUnits.manageUnit.root).not.toBeVisible();
await poOmnichannelUnits.search(unitName);
await expect(poOmnichannelUnits.table.findRowByName(unitName)).toBeVisible();
});
await test.step('expect to delete unit', async () => {
await poOmnichannelUnits.deleteUnit(unitName);
await expect(poOmnichannelUnits.table.findRowByName(unitName)).toHaveCount(0);
});
});
test('OC - Manage Units - Edit unit name', async ({ api }) => {
const unitName = faker.string.uuid();
const editedUnitName = faker.string.uuid();
const unit = await test.step('expect to create new unit', async () => {
const { data: newUnit } = await createOrUpdateUnit(api, {
name: unitName,
visibility: 'public',
monitors: [{ monitorId: monitor.data._id, username: 'user2' }],
departments: [{ departmentId: department.data._id }],
});
return newUnit;
});
await poOmnichannelUnits.goto();
await test.step('expect to edit unit', async () => {
await poOmnichannelUnits.search(unit.name);
await poOmnichannelUnits.table.findRowByName(unit.name).click();
await expect(poOmnichannelUnits.manageUnit.root).toBeVisible();
await poOmnichannelUnits.manageUnit.inputName.fill(editedUnitName);
await poOmnichannelUnits.manageUnit.btnSave.click();
await expect(poOmnichannelUnits.manageUnit.root).not.toBeVisible();
await expect(poOmnichannelUnits.inputSearch).toBeVisible();
await poOmnichannelUnits.search(editedUnitName);
await expect(poOmnichannelUnits.table.findRowByName(editedUnitName)).toBeVisible();
});
await test.step('expect to add another monitor to list', async () => {
await poOmnichannelUnits.table.findRowByName(editedUnitName).click();
await poOmnichannelUnits.manageUnit.selectMonitor('user3');
await poOmnichannelUnits.manageUnit.btnSave.click();
await expect(poOmnichannelUnits.manageUnit.root).not.toBeVisible();
await poOmnichannelUnits.search(editedUnitName);
await poOmnichannelUnits.table.findRowByName(editedUnitName).click();
await expect(poOmnichannelUnits.manageUnit.findMonitorChipOption('user2')).toBeVisible();
await expect(poOmnichannelUnits.manageUnit.findMonitorChipOption('user3')).toBeVisible();
});
await test.step('expect unit to remove one of the two monitors', async () => {
await poOmnichannelUnits.search(editedUnitName);
await poOmnichannelUnits.table.findRowByName(editedUnitName).click();
await poOmnichannelUnits.manageUnit.removeMonitor('user2');
await poOmnichannelUnits.manageUnit.btnSave.click();
await expect(poOmnichannelUnits.manageUnit.root).not.toBeVisible();
await poOmnichannelUnits.search(editedUnitName);
await poOmnichannelUnits.table.findRowByName(editedUnitName).click();
await expect(poOmnichannelUnits.manageUnit.findMonitorChipOption('user3')).toBeVisible();
await expect(poOmnichannelUnits.manageUnit.findMonitorChipOption('user2')).toBeHidden();
});
await test.step('expect to delete unit', async () => {
await poOmnichannelUnits.table.findRowByName(editedUnitName).click();
await expect(poOmnichannelUnits.manageUnit.root).toBeVisible();
await poOmnichannelUnits.deleteUnit(editedUnitName);
await expect(poOmnichannelUnits.table.findRowByName(editedUnitName)).not.toBeVisible();
});
});
test('OC - Manage Units - Edit unit departments', async ({ api, page }) => {
const unit = await test.step('expect to create new unit', async () => {
const { data: unit } = await createOrUpdateUnit(api, {
name: faker.string.uuid(),
visibility: 'public',
monitors: [{ monitorId: monitor.data._id, username: 'user2' }],
departments: [{ departmentId: department.data._id }],
});
return unit;
});
await page.reload();
await test.step('expect to add unit departments', async () => {
await poOmnichannelUnits.search(unit.name);
await poOmnichannelUnits.table.findRowByName(unit.name).click();
await expect(poOmnichannelUnits.manageUnit.root).toBeVisible();
await poOmnichannelUnits.manageUnit.selectDepartment(department2.data.name);
await poOmnichannelUnits.manageUnit.btnSave.click();
await expect(poOmnichannelUnits.manageUnit.root).not.toBeVisible();
await poOmnichannelUnits.search(unit.name);
await poOmnichannelUnits.table.findRowByName(unit.name).click();
await expect(poOmnichannelUnits.manageUnit.root).toBeVisible();
await expect(poOmnichannelUnits.manageUnit.findDepartmentsChipOption(department2.data.name)).toBeVisible();
await poOmnichannelUnits.manageUnit.findDepartmentsChipOption(department2.data.name).hover();
await expect(page.getByRole('tooltip', { name: department2.data.name })).toBeVisible();
await poOmnichannelUnits.manageUnit.close();
});
await test.step('expect to remove unit departments', async () => {
await poOmnichannelUnits.search(unit.name);
await poOmnichannelUnits.table.findRowByName(unit.name).click();
await expect(poOmnichannelUnits.manageUnit.root).toBeVisible();
await poOmnichannelUnits.manageUnit.selectDepartment(department2.data.name);
await poOmnichannelUnits.manageUnit.btnSave.click();
await expect(poOmnichannelUnits.manageUnit.root).not.toBeVisible();
await poOmnichannelUnits.search(unit.name);
await poOmnichannelUnits.table.findRowByName(unit.name).click();
await expect(poOmnichannelUnits.manageUnit.root).toBeVisible();
await expect(poOmnichannelUnits.manageUnit.findDepartmentsChipOption(department2.data.name)).toBeHidden();
await poOmnichannelUnits.manageUnit.close();
});
await test.step('expect to delete unit', async () => {
await poOmnichannelUnits.search(unit.name);
await poOmnichannelUnits.table.findRowByName(unit.name).click();
await expect(poOmnichannelUnits.manageUnit.root).toBeVisible();
await poOmnichannelUnits.deleteUnit(unit.name);
await expect(poOmnichannelUnits.table.findRowByName(unit.name)).not.toBeVisible();
});
});
});