From fa214a4691495cf1371dc7abe4550d4c7f4c1c66 Mon Sep 17 00:00:00 2001 From: Ravi Chandra Sekhar Sarika Date: Thu, 18 Dec 2025 22:57:30 +0530 Subject: [PATCH 1/8] fix: e2e tests --- playwright/Utils/helperUtils.ts | 172 +++++++++--------- playwright/Utils/wrapupUtils.ts | 14 +- playwright/test-manager.ts | 61 ++++++- ...nce-task-control-combinations-test.spec.ts | 13 +- .../tests/advanced-task-controls-test.spec.ts | 78 ++++---- ...ng-task-and-controls-multi-session.spec.ts | 18 +- playwright/tests/station-login-test.spec.ts | 6 +- playwright/tests/tasklist-test.spec.ts | 19 +- .../cc/samples-cc-react-app/src/App.tsx | 8 +- 9 files changed, 238 insertions(+), 151 deletions(-) diff --git a/playwright/Utils/helperUtils.ts b/playwright/Utils/helperUtils.ts index cede87b13..bf25d3a2f 100644 --- a/playwright/Utils/helperUtils.ts +++ b/playwright/Utils/helperUtils.ts @@ -315,130 +315,128 @@ export function isColorClose(receivedColor: string, expectedColor: ThemeColor, t export const handleStrayTasks = async ( page: Page, extensionPage: Page | null = null, - maxIterations: number = 10 + maxIterations: number = 20 ): Promise => { - await page.waitForTimeout(1000); + const startTime = Date.now(); + const timestamp = () => `[${new Date().toISOString()}]`; + // Quick check for state-select visibility const stateSelectVisible = await page .getByTestId('state-select') - .waitFor({state: 'visible', timeout: 30000}) - .then(() => true) + .isVisible() .catch(() => false); if (stateSelectVisible) { const ronapopupVisible = await page .getByTestId('samples:rona-popup') - .waitFor({state: 'visible', timeout: AWAIT_TIMEOUT}) - .then(() => true) + .isVisible() .catch(() => false); if (ronapopupVisible) { + console.log(`handleStrayTasks: Submitting RONA popup`); await submitRonaPopup(page, RONA_OPTIONS.AVAILABLE); } await changeUserState(page, USER_STATES.AVAILABLE); - await page.waitForTimeout(4000); } - const incomingTaskDiv = page.getByTestId(/^samples:incoming-task(-\w+)?$/); - - let iterations = 0; - while (iterations < maxIterations) { - iterations++; - let flag1 = false; - let flag2 = true; - // Check if there's actually anything to handle before processing - const hasIncomingTask = await incomingTaskDiv - .first() - .isVisible() - .catch(() => false); - const hasEndButton = await page - .getByTestId('call-control:end-call') - .first() - .isVisible() - .catch(() => false); - const hasWrapupButton = await page - .getByTestId('call-control:wrapup-button') - .first() - .isVisible() - .catch(() => false); + const incomingTaskDiv = page.getByTestId(/^samples:incoming-task(-\w+)?$/); - if (!hasIncomingTask && !hasEndButton && !hasWrapupButton) { - // Nothing to handle, exit early + let tasksAccepted = 0; + let tasksCleared = 0; + + // ============================================ + // PHASE 1: Accept all incoming tasks first + // ============================================ + console.log(`handleStrayTasks: Phase 1 - Accepting incoming tasks`); + let acceptIterations = 0; + while (acceptIterations < maxIterations) { + acceptIterations++; + + const task = incomingTaskDiv.first(); + const isTaskVisible = await task.isVisible().catch(() => false); + if (!isTaskVisible) { + console.log(`handleStrayTasks: No more incoming tasks to accept`); break; } - // Inner task acceptance loop with timeout protection - let taskAttempts = 0; - const maxTaskAttempts = 5; - - while (taskAttempts < maxTaskAttempts) { - taskAttempts++; - const task = incomingTaskDiv.first(); - let isTaskVisible = await task.isVisible().catch(() => false); - if (!isTaskVisible) break; + const acceptButton = task.getByTestId('task:accept-button').first(); + const taskText = await task.innerText().catch(() => ''); + const isExtensionCall = taskText.includes('Ringing...'); - const acceptButton = task.getByTestId('task:accept-button').first(); - const acceptButtonVisible = await acceptButton.isVisible().catch(() => false); - const isExtensionCall = await (await task.innerText()).includes('Ringing...'); - - if (isExtensionCall) { - if (!extensionPage) { - throw new Error('Extension page is not available for handling extension call'); - } - const extensionCallVisible = await extensionPage - .locator('[data-test="right-action-button"]') - .waitFor({state: 'visible', timeout: 40000}) // Restored original timeout - .then(() => true) - .catch(() => false); - if (extensionCallVisible) { - await acceptExtensionCall(extensionPage); - flag1 = true; - } else { - console.warn('Extension call timeout - skipping task'); - break; // Skip this task instead of throwing error - } + if (isExtensionCall) { + if (!extensionPage) { + throw new Error('Extension page is not available for handling extension call'); + } + console.log(`handleStrayTasks: Handling extension call`); + const extensionCallVisible = await extensionPage + .locator('[data-test="right-action-button"]') + .waitFor({state: 'visible', timeout: 40000}) + .then(() => true) + .catch(() => false); + if (extensionCallVisible) { + await acceptExtensionCall(extensionPage); + tasksAccepted++; + console.log(`handleStrayTasks: Accepted extension call (${tasksAccepted} total)`); } else { + console.warn(`handleStrayTasks: Extension call timeout - skipping task`); + } + } else { + const acceptVisible = await acceptButton.isVisible().catch(() => false); + if (acceptVisible) { try { await acceptButton.click({timeout: AWAIT_TIMEOUT}); - flag1 = true; + tasksAccepted++; + console.log(`handleStrayTasks: Accepted incoming task (${tasksAccepted} total)`); } catch (error) { - console.warn('Failed to click accept button:', error); + console.warn(`handleStrayTasks: Failed to click accept button:`, error); } + } else { + // Task visible but no accept button - might already be accepted, break + break; } - await page.waitForTimeout(1000); } + // Small wait for UI to update + await page.waitForTimeout(200); + } + + // ============================================ + // PHASE 2: Clear all pending calls/wrapups + // ============================================ + console.log(`andleStrayTasks: Phase 2 - Clearing pending calls/wrapups`); + let clearIterations = 0; + while (clearIterations < maxIterations) { + clearIterations++; + const endButton = page.getByTestId('call-control:end-call').first(); - const endButtonVisible = await endButton - .waitFor({state: 'visible', timeout: 2000}) - .then(() => true) - .catch(() => false); - if (endButtonVisible) { - await page.waitForTimeout(2000); - await endButton.click({timeout: AWAIT_TIMEOUT}); - await submitWrapup(page, WRAPUP_REASONS.SALE); - } else { - const wrapupBox = page.getByTestId('call-control:wrapup-button').first(); - const isWrapupBoxVisible = await wrapupBox - .waitFor({state: 'visible', timeout: 2000}) - .then(() => true) - .catch(() => false); - if (isWrapupBoxVisible) { - await page.waitForTimeout(2000); - await submitWrapup(page, WRAPUP_REASONS.SALE); - await page.waitForTimeout(2000); - } else { - flag2 = false; - } - } + const wrapupBox = page.getByTestId('call-control:wrapup-button').first(); + const endButtonVisible = await endButton.isVisible().catch(() => false); + const wrapupBoxVisible = await wrapupBox.isVisible().catch(() => false); - if (!flag1 && !flag2) { + if (!endButtonVisible && !wrapupBoxVisible) { + console.log(`handleStrayTasks: No more pending calls/wrapups`); break; } + + console.log( + `handleStrayTasks: Clearing task (iteration ${clearIterations}) - ` + + `endButton: ${endButtonVisible}, wrapupButton: ${wrapupBoxVisible}` + ); + + await clearPendingCallAndWrapup(page); + tasksCleared++; + console.log(`handleStrayTasks: Cleared task (${tasksCleared} total)`); + // Small wait for UI to update + await page.waitForTimeout(200); } - console.log(`Completed stray task handling after ${iterations} iterations`); + const duration = Date.now() - startTime; + const totalHandled = tasksAccepted + tasksCleared; + console.log( + `handleStrayTasks: Completed in ${duration}ms - ` + + `${tasksAccepted} task(s) accepted, ${tasksCleared} task(s) cleared, ${totalHandled} total` + ); }; /** diff --git a/playwright/Utils/wrapupUtils.ts b/playwright/Utils/wrapupUtils.ts index 5ab4ebfaf..862c40571 100644 --- a/playwright/Utils/wrapupUtils.ts +++ b/playwright/Utils/wrapupUtils.ts @@ -12,6 +12,11 @@ export async function submitWrapup(page: Page, reason: WrapupReason): Promise true) .catch(() => false); if (!isWrapupBoxVisible) throw new Error('Wrapup box is not visible'); - await wrapupBox.first().click({timeout: AWAIT_TIMEOUT}); - await page.waitForTimeout(UI_SETTLE_TIMEOUT); + + // Check if dropdown is already open (aria-expanded="true") + const isAlreadyOpen = await wrapupBox.first().getAttribute('aria-expanded') === 'true'; + if (!isAlreadyOpen) { + await wrapupBox.first().click({timeout: AWAIT_TIMEOUT}); + await page.waitForTimeout(UI_SETTLE_TIMEOUT); + } await expect(page.getByTestId('call-control:wrapup-select').first()).toBeVisible({timeout: AWAIT_TIMEOUT}); await page.getByTestId('call-control:wrapup-select').first().click({timeout: AWAIT_TIMEOUT}); await page.waitForTimeout(UI_SETTLE_TIMEOUT); diff --git a/playwright/test-manager.ts b/playwright/test-manager.ts index 20f0a8738..6acdc6e74 100644 --- a/playwright/test-manager.ts +++ b/playwright/test-manager.ts @@ -591,15 +591,18 @@ export class TestManager { await Promise.all(cleanupOperations); } - // Helper method to hard-reset the dial number login session - public async resetDialNumberSession(): Promise { - if (!this.dialNumberPage || !this.dialNumberContext) { + // Helper method to hard-reset a session (caller, dialNumber, or extension) + public async resetSession(sessionType: 'caller' | 'dialNumber' | 'extension'): Promise { + const {page, context, username, password, label} = this.getSessionConfig(sessionType); + + if (!page || !context) { return; } + const envTokens = this.getEnvTokens(); try { - await this.dialNumberContext.clearCookies(); - await this.dialNumberPage.evaluate(() => { + await context.clearCookies(); + await page.evaluate(() => { try { localStorage.clear(); } catch {} @@ -608,11 +611,51 @@ export class TestManager { } catch {} }); // Navigate fresh and login again - await this.dialNumberPage.goto(CALL_URL); - await loginExtension(this.dialNumberPage, envTokens.dialNumberUsername!, envTokens.dialNumberPassword!); - await this.enforceSingleDialNumberInOwnContext(); + await page.goto(CALL_URL); + await loginExtension(page, username(envTokens), password(envTokens)); + + // Enforce single page for dialNumber context + if (sessionType === 'dialNumber') { + await this.enforceSingleDialNumberInOwnContext(); + } } catch (error) { - throw new Error(`Failed to reset dial number session: ${error}`); + throw new Error(`Failed to reset ${label} session: ${error}`); + } + } + + // Helper to get session configuration based on type + private getSessionConfig(sessionType: 'caller' | 'dialNumber' | 'extension'): { + page: Page | undefined; + context: BrowserContext | undefined; + username: (env: EnvTokens) => string; + password: (env: EnvTokens) => string; + label: string; + } { + switch (sessionType) { + case 'caller': + return { + page: this.callerPage, + context: this.callerExtensionContext, + username: (env) => env.agent2Username, + password: (env) => env.password, + label: 'caller', + }; + case 'dialNumber': + return { + page: this.dialNumberPage, + context: this.dialNumberContext, + username: (env) => env.dialNumberUsername!, + password: (env) => env.dialNumberPassword!, + label: 'dial number', + }; + case 'extension': + return { + page: this.agent1ExtensionPage, + context: this.extensionContext, + username: (env) => env.agent1Username, + password: (env) => env.password, + label: 'extension', + }; } } diff --git a/playwright/tests/advance-task-control-combinations-test.spec.ts b/playwright/tests/advance-task-control-combinations-test.spec.ts index 029bab244..c4c217acf 100644 --- a/playwright/tests/advance-task-control-combinations-test.spec.ts +++ b/playwright/tests/advance-task-control-combinations-test.spec.ts @@ -11,7 +11,7 @@ import {changeUserState, verifyCurrentState} from '../Utils/userStateUtils'; import {createCallTask, acceptIncomingTask, acceptExtensionCall, endCallTask} from '../Utils/incomingTaskUtils'; import {submitWrapup} from '../Utils/wrapupUtils'; import {USER_STATES, TASK_TYPES, WRAPUP_REASONS} from '../constants'; -import {waitForState, clearPendingCallAndWrapup} from '../Utils/helperUtils'; +import {waitForState, clearPendingCallAndWrapup, handleStrayTasks} from '../Utils/helperUtils'; import {endTask, holdCallToggle} from '../Utils/taskControlUtils'; import {TestManager} from '../test-manager'; @@ -25,6 +25,11 @@ export default function createAdvanceCombinationsTests() { await testManager.setupForAdvancedCombinations(browser); }); + test.beforeEach(async () => { + await handleStrayTasks(testManager.agent1Page); + await handleStrayTasks(testManager.agent2Page); + }); + test('Transfer from one agent to another, then transfer back to the first agent', async () => { await changeUserState(testManager.agent2Page, USER_STATES.MEETING); await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); @@ -274,7 +279,7 @@ export default function createAdvanceCombinationsTests() { test('Dial Number: consult then end consult returns UI to normal', async () => { test.skip(!process.env.PW_DIAL_NUMBER_NAME, 'PW_DIAL_NUMBER_NAME not set'); - await testManager.resetDialNumberSession(); + await testManager.resetSession('dialNumber'); await changeUserState(testManager.agent2Page, USER_STATES.MEETING); await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); await createCallTask(testManager.callerPage!, process.env[`${testManager.projectName}_ENTRY_POINT`]!); @@ -295,7 +300,7 @@ export default function createAdvanceCombinationsTests() { test('Dial Number: consult then transfer completes and remote ends', async () => { test.skip(!process.env.PW_DIAL_NUMBER_NAME, 'PW_DIAL_NUMBER_NAME not set'); - await testManager.resetDialNumberSession(); + await testManager.resetSession('dialNumber'); await changeUserState(testManager.agent2Page, USER_STATES.MEETING); await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); await createCallTask(testManager.callerPage!, process.env[`${testManager.projectName}_ENTRY_POINT`]!); @@ -338,7 +343,7 @@ export default function createAdvanceCombinationsTests() { await clearPendingCallAndWrapup(testManager.agent1Page); await clearPendingCallAndWrapup(testManager.agent2Page); - await testManager.resetDialNumberSession(); + await testManager.resetSession('dialNumber'); await changeUserState(testManager.agent2Page, USER_STATES.MEETING); await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); await createCallTask(testManager.callerPage!, process.env[`${testManager.projectName}_ENTRY_POINT`]!); diff --git a/playwright/tests/advanced-task-controls-test.spec.ts b/playwright/tests/advanced-task-controls-test.spec.ts index 4b554e8bc..9c36babec 100644 --- a/playwright/tests/advanced-task-controls-test.spec.ts +++ b/playwright/tests/advanced-task-controls-test.spec.ts @@ -23,6 +23,7 @@ import {submitWrapup} from '../Utils/wrapupUtils'; import {USER_STATES, TASK_TYPES, WRAPUP_REASONS} from '../constants'; import {holdCallToggle, endTask, verifyHoldButtonIcon, verifyTaskControls} from '../Utils/taskControlUtils'; import {TestManager} from '../test-manager'; +import {handleStrayTasks} from '../Utils/helperUtils'; // Extract test functions for cleaner syntax const {describe, beforeAll, afterAll, beforeEach} = test; @@ -50,6 +51,11 @@ export default function createAdvancedTaskControlsTests() { await testManager.cleanup(); }); + test.beforeEach(async () => { + await handleStrayTasks(testManager.agent1Page); + await handleStrayTasks(testManager.agent2Page); + }); + // ============================================================================= // BLIND TRANSFER TESTS // ============================================================================= @@ -84,12 +90,13 @@ export default function createAdvancedTaskControlsTests() { ); // Verify transfer success in console logs - await testManager.agent1Page.waitForTimeout(3000); + await testManager.agent1Page.bringToFront(); verifyTransferSuccessLogs(); // Verify Agent 1 goes to wrapup state await submitWrapup(testManager.agent1Page, WRAPUP_REASONS.SALE); // Agent 2 should receive the transfer and accept it + await testManager.agent2Page.bringToFront(); const incomingTransferDiv = testManager.agent2Page.getByTestId('samples:incoming-task-telephony').first(); await incomingTransferDiv.waitFor({state: 'visible', timeout: 60000}); @@ -140,7 +147,7 @@ export default function createAdvancedTaskControlsTests() { }); test('Call Blind Transferred to DialNumber', async () => { - await testManager.resetDialNumberSession(); + await testManager.resetSession('dialNumber'); await consultOrTransfer(testManager.agent1Page, 'dialNumber', 'transfer', process.env.PW_DIAL_NUMBER_NAME); //DialNumber accepts the transfer @@ -158,9 +165,9 @@ export default function createAdvancedTaskControlsTests() { }); test('Call Blind Transferred to Queue with DialNumber', async () => { - await testManager.resetDialNumberSession(); + await testManager.resetSession('dialNumber'); // First transfer from Agent 1 to Agent 2 - await consultOrTransfer(testManager.agent1Page, 'queue', 'transfer', 'queue with dn'); + await consultOrTransfer(testManager.agent1Page, 'queue', 'transfer', 'queue with dn e2e'); //DialNumber accepts the transfer await ensureDialNumberLoggedIn(testManager.dialNumberPage); @@ -182,32 +189,12 @@ export default function createAdvancedTaskControlsTests() { // ============================================================================= describe('Consult and Consult Transfer Scenarios', () => { - test('Entry Point Consult: visible and functional only for supported users (no blind transfer)', async ({}, testInfo) => { - await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); - await createCallTask(testManager.callerPage!, process.env[`${testManager.projectName}_ENTRY_POINT`]!); - const incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-telephony').first(); - await incomingTaskDiv.waitFor({state: 'visible', timeout: 80000}); - await acceptExtensionCall(testManager.agent1ExtensionPage); - await testManager.agent1Page.waitForTimeout(3000); - await verifyCurrentState(testManager.agent1Page, USER_STATES.ENGAGED); - - // Ensure consult UI and Entry Point tab exists - await testManager.agent1Page.getByTestId('call-control:consult').nth(1).click(); - await expect(testManager.agent1Page.locator('#consult-search')).toBeVisible(); - await testManager.agent1Page.getByRole('button', {name: 'Entry Point'}).click(); - - await consultOrTransfer(testManager.agent1Page, 'entryPoint', 'consult', process.env.PW_ENTRYPOINT_NAME!); - await expect(testManager.agent1Page.getByTestId('cancel-consult-btn')).toBeVisible(); - await testManager.agent1Page.waitForTimeout(1000); - await cancelConsult(testManager.agent1Page); - await testManager.agent1Page.waitForTimeout(1000); - await verifyCurrentState(testManager.agent1Page, USER_STATES.ENGAGED); - }); test('Agent Consult Transfer: cancel, decline, timeout, and transfer scenarios are handled correctly in sequence', async () => { // ...existing code for Agent Consult Transfer test... await changeUserState(testManager.agent2Page, USER_STATES.MEETING); await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); await createCallTask(testManager.callerPage!, process.env[`${testManager.projectName}_ENTRY_POINT`]!); + await testManager.agent1Page.bringToFront(); const incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-telephony').first(); await incomingTaskDiv.waitFor({state: 'visible', timeout: 80000}); await testManager.agent1ExtensionPage @@ -215,7 +202,7 @@ export default function createAdvancedTaskControlsTests() { .waitFor({state: 'visible', timeout: 20000}); await acceptExtensionCall(testManager.agent1ExtensionPage); await changeUserState(testManager.agent2Page, USER_STATES.AVAILABLE); - await testManager.agent1Page.waitForTimeout(5000); + await testManager.agent1Page.waitForTimeout(3000); await verifyCurrentState(testManager.agent1Page, USER_STATES.ENGAGED); // 1. Accept consult and end @@ -252,7 +239,7 @@ export default function createAdvancedTaskControlsTests() { ); const consultRequestDiv2 = testManager.agent2Page.getByTestId('samples:incoming-task-telephony').first(); await consultRequestDiv2.waitFor({state: 'visible', timeout: 60000}); - await testManager.agent2Page.waitForTimeout(3000); + await testManager.agent2Page.bringToFront(); await declineIncomingTask(testManager.agent2Page, TASK_TYPES.CALL); await verifyTaskControls(testManager.agent1Page, TASK_TYPES.CALL); await verifyHoldButtonIcon(testManager.agent1Page, {expectedIsHeld: true}); @@ -270,7 +257,7 @@ export default function createAdvancedTaskControlsTests() { 'consult', process.env[`${testManager.projectName}_AGENT2_NAME`]! ); - await testManager.agent1Page.waitForTimeout(20000); // Wait for timeout + await testManager.agent1Page.waitForTimeout(10000); await verifyTaskControls(testManager.agent1Page, TASK_TYPES.CALL); await verifyHoldButtonIcon(testManager.agent1Page, {expectedIsHeld: true}); await holdCallToggle(testManager.agent1Page); @@ -317,6 +304,7 @@ export default function createAdvancedTaskControlsTests() { await acceptExtensionCall(testManager.agent1ExtensionPage); await testManager.agent1Page.waitForTimeout(5000); await verifyCurrentState(testManager.agent1Page, USER_STATES.ENGAGED); + await changeUserState(testManager.agent2Page, USER_STATES.AVAILABLE); // 1. Cancel consult clearAdvancedCapturedLogs(); @@ -332,8 +320,6 @@ export default function createAdvancedTaskControlsTests() { await verifyTaskControls(testManager.agent1Page, TASK_TYPES.CALL); await expect(testManager.agent1Page.getByTestId('cancel-consult-btn')).not.toBeVisible(); - // 2. Accept consult and end (Agent 2 accepts, Agent 1 ends) - await changeUserState(testManager.agent2Page, USER_STATES.AVAILABLE); clearAdvancedCapturedLogs(); await consultOrTransfer( testManager.agent1Page, @@ -346,7 +332,7 @@ export default function createAdvancedTaskControlsTests() { const consultRequestDiv1 = testManager.agent2Page.getByTestId('samples:incoming-task-telephony').first(); await consultRequestDiv1.waitFor({state: 'visible', timeout: 60000}); await acceptIncomingTask(testManager.agent2Page, TASK_TYPES.CALL); - await testManager.agent2Page.waitForTimeout(3000); + await testManager.agent2Page.bringToFront(); await cancelConsult(testManager.agent1Page); await testManager.agent1Page.waitForTimeout(3000); await verifyCurrentState(testManager.agent2Page, USER_STATES.AVAILABLE); @@ -389,9 +375,9 @@ export default function createAdvancedTaskControlsTests() { const consultRequestDiv3 = testManager.agent2Page.getByTestId('samples:incoming-task-telephony').first(); await consultRequestDiv3.waitFor({state: 'visible', timeout: 60000}); await acceptIncomingTask(testManager.agent2Page, TASK_TYPES.CALL); - await testManager.agent2Page.waitForTimeout(3000); + await testManager.agent2Page.bringToFront(); await testManager.agent1Page.getByTestId('transfer-consult-btn').click(); - await testManager.agent1Page.waitForTimeout(2000); + await testManager.agent1Page.bringToFront(); await submitWrapup(testManager.agent1Page, WRAPUP_REASONS.SALE); await verifyCurrentState(testManager.agent2Page, USER_STATES.ENGAGED); await verifyTaskControls(testManager.agent2Page, TASK_TYPES.CALL); @@ -446,7 +432,7 @@ export default function createAdvancedTaskControlsTests() { await testManager.agent1Page.waitForTimeout(2000); verifyConsultStartSuccessLogs(); await acceptExtensionCall(testManager.dialNumberPage); - await testManager.agent1Page.waitForTimeout(2000); + await testManager.agent1Page.bringToFront(); await cancelConsult(testManager.agent1Page); await verifyTaskControls(testManager.agent1Page, TASK_TYPES.CALL); await testManager.agent1Page.waitForTimeout(2000); @@ -511,9 +497,31 @@ export default function createAdvancedTaskControlsTests() { // End call and complete wrapup to clean up for next tests await endTask(testManager.agent1Page); - await testManager.agent1Page.waitForTimeout(2000); + await testManager.agent1Page.bringToFront(); await submitWrapup(testManager.agent1Page, WRAPUP_REASONS.SALE); await testManager.agent1Page.waitForTimeout(1000); }); }); + + test('Entry Point Consult: visible and functional only for supported users (no blind transfer)', async ({}, testInfo) => { + await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); + await createCallTask(testManager.callerPage!, process.env[`${testManager.projectName}_ENTRY_POINT`]!); + const incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-telephony').first(); + await incomingTaskDiv.waitFor({state: 'visible', timeout: 80000}); + await acceptExtensionCall(testManager.agent1ExtensionPage); + await testManager.agent1Page.waitForTimeout(3000); + await verifyCurrentState(testManager.agent1Page, USER_STATES.ENGAGED); + + // Ensure consult UI and Entry Point tab exists + await testManager.agent1Page.getByTestId('call-control:consult').nth(1).click(); + await expect(testManager.agent1Page.locator('#consult-search')).toBeVisible(); + await testManager.agent1Page.getByRole('button', {name: 'Entry Point'}).click(); + + await consultOrTransfer(testManager.agent1Page, 'entryPoint', 'consult', process.env.PW_ENTRYPOINT_NAME!); + await expect(testManager.agent1Page.getByTestId('cancel-consult-btn')).toBeVisible(); + await testManager.agent1Page.waitForTimeout(1000); + await cancelConsult(testManager.agent1Page); + await testManager.agent1Page.waitForTimeout(1000); + await verifyCurrentState(testManager.agent1Page, USER_STATES.ENGAGED); + }); } diff --git a/playwright/tests/incoming-task-and-controls-multi-session.spec.ts b/playwright/tests/incoming-task-and-controls-multi-session.spec.ts index dfd308e70..07ed8f421 100644 --- a/playwright/tests/incoming-task-and-controls-multi-session.spec.ts +++ b/playwright/tests/incoming-task-and-controls-multi-session.spec.ts @@ -35,8 +35,8 @@ export default function createIncomingTaskAndControlsMultiSessionTests() { test('should handle multi-session incoming call with state synchronization', async () => { await createCallTask(testManager.callerPage, process.env[`${testManager.projectName}_ENTRY_POINT`]!); await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); - const incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-telephony').first(); - const incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-telephony').first(); + let incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-telephony').first(); + let incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-telephony').first(); await incomingTaskDiv.waitFor({state: 'visible', timeout: 40000}); await testManager.agent1ExtensionPage.waitForTimeout(2000); await testManager.agent1ExtensionPage @@ -64,12 +64,14 @@ export default function createIncomingTaskAndControlsMultiSessionTests() { await waitForState(testManager.multiSessionAgent1Page, USER_STATES.AVAILABLE); await testManager.multiSessionAgent1Page.waitForTimeout(2000); await verifyCurrentState(testManager.multiSessionAgent1Page, USER_STATES.AVAILABLE); + incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-telephony').first(); await incomingTaskDiv.waitFor({state: 'visible', timeout: 10000}); await testManager.agent1ExtensionPage.waitForTimeout(2000); await testManager.agent1ExtensionPage .locator('[data-test="generic-person-item-base"]') .first() .waitFor({state: 'visible', timeout: 10000}); + incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-telephony').first(); await incomingTaskDiv2.waitFor({state: 'visible', timeout: 10000}); await testManager.agent1Page.waitForTimeout(2000); await acceptExtensionCall(testManager.agent1ExtensionPage); @@ -220,8 +222,8 @@ export default function createIncomingTaskAndControlsMultiSessionTests() { test('should handle multi-session incoming chat with state synchronization', async () => { await createChatTask(testManager.chatPage, process.env[`${testManager.projectName}_CHAT_URL`]!); await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); - const incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-chat').first(); - const incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-chat').first(); + let incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-chat').first(); + let incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-chat').first(); await incomingTaskDiv.waitFor({state: 'visible', timeout: 60000}); await incomingTaskDiv2.waitFor({state: 'visible', timeout: 10000}); await incomingTaskDiv.waitFor({state: 'hidden', timeout: 30000}); @@ -240,6 +242,8 @@ export default function createIncomingTaskAndControlsMultiSessionTests() { await waitForState(testManager.multiSessionAgent1Page, USER_STATES.AVAILABLE); await testManager.multiSessionAgent1Page.waitForTimeout(2000); await verifyCurrentState(testManager.multiSessionAgent1Page, USER_STATES.AVAILABLE); + incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-chat').first(); + incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-chat').first(); await incomingTaskDiv.waitFor({state: 'visible', timeout: 10000}); await incomingTaskDiv2.waitFor({state: 'visible', timeout: 10000}); await acceptIncomingTask(testManager.agent1Page, TASK_TYPES.CHAT); @@ -267,8 +271,8 @@ export default function createIncomingTaskAndControlsMultiSessionTests() { test('should handle multi-session incoming email with state synchronization', async () => { await createEmailTask(process.env[`${testManager.projectName}_EMAIL_ENTRY_POINT`]!); await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE); - const incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-email').first(); - const incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-email').first(); + let incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-email').first(); + let incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-email').first(); await incomingTaskDiv.waitFor({state: 'visible', timeout: 50000}); await incomingTaskDiv2.waitFor({state: 'visible', timeout: 10000}); await incomingTaskDiv.waitFor({state: 'hidden', timeout: 30000}); @@ -289,6 +293,8 @@ export default function createIncomingTaskAndControlsMultiSessionTests() { await waitForState(testManager.agent1Page, USER_STATES.AVAILABLE); await testManager.multiSessionAgent1Page.waitForTimeout(2000); await verifyCurrentState(testManager.multiSessionAgent1Page, USER_STATES.AVAILABLE); + incomingTaskDiv = testManager.agent1Page.getByTestId('samples:incoming-task-email').first(); + incomingTaskDiv2 = testManager.multiSessionAgent1Page.getByTestId('samples:incoming-task-email').first(); await incomingTaskDiv.waitFor({state: 'visible', timeout: 15000}); await incomingTaskDiv2.waitFor({state: 'visible', timeout: 15000}); await acceptIncomingTask(testManager.agent1Page, TASK_TYPES.EMAIL); diff --git a/playwright/tests/station-login-test.spec.ts b/playwright/tests/station-login-test.spec.ts index 86caf56e0..d8137edfe 100644 --- a/playwright/tests/station-login-test.spec.ts +++ b/playwright/tests/station-login-test.spec.ts @@ -305,17 +305,17 @@ export default function createStationLoginTests() { // Uncheck - Desktop should be visible await hideDesktopCheckbox.click(); await testManager.agent1Page.waitForTimeout(500); - await verifyDesktopOptionVisibility(testManager.agent1Page, true); + await verifyDesktopOptionVisibility(testManager.agent1Page, false); // Check - Desktop should be hidden await hideDesktopCheckbox.click(); await testManager.agent1Page.waitForTimeout(500); - await verifyDesktopOptionVisibility(testManager.agent1Page, false); + await verifyDesktopOptionVisibility(testManager.agent1Page, true); // Uncheck again - Desktop should be visible again await hideDesktopCheckbox.click(); await testManager.agent1Page.waitForTimeout(500); - await verifyDesktopOptionVisibility(testManager.agent1Page, true); + await verifyDesktopOptionVisibility(testManager.agent1Page, false); }); }); diff --git a/playwright/tests/tasklist-test.spec.ts b/playwright/tests/tasklist-test.spec.ts index 5b594c09a..016e6916c 100644 --- a/playwright/tests/tasklist-test.spec.ts +++ b/playwright/tests/tasklist-test.spec.ts @@ -46,18 +46,31 @@ async function getCurrentHandleTime(page: Page, index: number = 0): Promise { + await testManager.agent1Page.bringToFront(); const timeoutMs = 60000, pollInterval = 2000; const start = Date.now(); - const type = testId.split('-').pop(); + while (Date.now() - start < timeoutMs) { const taskDiv = testManager.agent1Page.getByTestId(testId).first(); const isVisible = await taskDiv.isVisible().catch(() => false); + if (isVisible) { + // Dismiss any open popovers that might be blocking + await testManager.agent1Page.keyboard.press('Escape'); + await testManager.agent1Page.waitForTimeout(200); + const acceptButton = taskDiv.getByTestId('task:accept-button').first(); + const acceptVisible = await acceptButton.isVisible().catch(() => false); + + if (!acceptVisible) { + await testManager.agent1Page.waitForTimeout(pollInterval); + continue; + } + await expect(acceptButton).toBeVisible({timeout: 5000}); + await expect(acceptButton).toBeEnabled({timeout: 5000}); await acceptButton.click({timeout: 3000}); - return; } await testManager.agent1Page.waitForTimeout(pollInterval); @@ -103,7 +116,7 @@ async function waitForConsoleLogs( const escTitle = escapeForRegExp(title!); const escMedia = escapeForRegExp(mediaType!); const pattern = new RegExp( - '^onTaskSelected invoked for task with title : ' + escTitle + ', and mediaType : ' + escMedia + '$' + '^onTaskSelected invoked for task with title : ' + escTitle + ', and mediaType : ' + escMedia ); const start = Date.now(); diff --git a/widgets-samples/cc/samples-cc-react-app/src/App.tsx b/widgets-samples/cc/samples-cc-react-app/src/App.tsx index 9fcc4b388..f54cc954d 100644 --- a/widgets-samples/cc/samples-cc-react-app/src/App.tsx +++ b/widgets-samples/cc/samples-cc-react-app/src/App.tsx @@ -180,8 +180,12 @@ function App() { const onTaskSelected = ({task, isClicked}) => { console.log('onTaskSelected invoked for task:', task, 'isClicked:', isClicked); + const callAssociatedDetails = task?.data?.interaction?.callAssociatedDetails; + const mediaType = task?.data?.interaction?.mediaType; + const isSocial = mediaType === 'social'; + const title = isSocial ? callAssociatedDetails?.customerName : callAssociatedDetails?.ani; console.log( - `onTaskSelected invoked for task with title : ${task?.data?.interaction?.callAssociatedDetails?.ani}, and mediaType : ${task?.data?.mediaType}` + `onTaskSelected invoked for task with title : ${title}, and mediaType : ${mediaType}` ); }; @@ -876,7 +880,7 @@ function App() { setCollapsedTasks((prev) => prev.filter((id) => id !== task.data.interactionId)); } }} - data-testid={`samples:incoming-task-${task.data.mediaType}`} + data-testid={`samples:incoming-task-${task.data.interaction?.mediaType}`} > <>