From 6b81885e4aaa83e3ddf4b596fa6cdf5b93a7d668 Mon Sep 17 00:00:00 2001 From: prayprab_cisco Date: Mon, 16 Jun 2025 18:42:27 +0530 Subject: [PATCH 001/104] Add User State Widget Functionality Test Suite - Comprehensive test for user state widget functionality - Includes authentication, state changes, theme validation - Tests page refresh persistence and multi-session sync - Validates callback sequences and idle state transitions --- .../contact-center/cc-components/package.json | 2 +- .../contact-center/cc-widgets/package.json | 2 +- .../contact-center/station-login/package.json | 2 +- packages/contact-center/store/package.json | 2 +- packages/contact-center/task/package.json | 2 +- .../contact-center/user-state/package.json | 2 +- playwright/UserStateTest.spec.ts | 585 ++++++++++++++++++ playwright/UserStateWidgetTest.spec.ts | 241 ++++++++ playwright/Utils/initUtils.ts | 44 ++ playwright/Utils/stateUtils.ts | 174 ++++++ playwright/Utils/stationLoginUtils.ts | 68 ++ 11 files changed, 1118 insertions(+), 6 deletions(-) create mode 100644 playwright/UserStateTest.spec.ts create mode 100644 playwright/UserStateWidgetTest.spec.ts create mode 100644 playwright/Utils/initUtils.ts create mode 100644 playwright/Utils/stateUtils.ts create mode 100644 playwright/Utils/stationLoginUtils.ts diff --git a/packages/contact-center/cc-components/package.json b/packages/contact-center/cc-components/package.json index 8e1b4d9ea..94110ba54 100644 --- a/packages/contact-center/cc-components/package.json +++ b/packages/contact-center/cc-components/package.json @@ -64,4 +64,4 @@ "react": ">=18.3.1", "react-dom": ">=18.3.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/cc-widgets/package.json b/packages/contact-center/cc-widgets/package.json index 75e77e442..cc227bf18 100644 --- a/packages/contact-center/cc-widgets/package.json +++ b/packages/contact-center/cc-widgets/package.json @@ -86,4 +86,4 @@ "^.+\\.(css|less|scss)$": "babel-jest" } } -} \ No newline at end of file +} diff --git a/packages/contact-center/station-login/package.json b/packages/contact-center/station-login/package.json index 50963d6d9..29f70593a 100644 --- a/packages/contact-center/station-login/package.json +++ b/packages/contact-center/station-login/package.json @@ -63,4 +63,4 @@ "react": ">=18.3.1", "react-dom": ">=18.3.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/store/package.json b/packages/contact-center/store/package.json index a9b7873f7..3d708220a 100644 --- a/packages/contact-center/store/package.json +++ b/packages/contact-center/store/package.json @@ -57,4 +57,4 @@ "webpack-cli": "5.1.4", "webpack-merge": "6.0.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/task/package.json b/packages/contact-center/task/package.json index 00e09cbfd..984ab9ce2 100644 --- a/packages/contact-center/task/package.json +++ b/packages/contact-center/task/package.json @@ -62,4 +62,4 @@ "react": ">=18.3.1", "react-dom": ">=18.3.1" } -} \ No newline at end of file +} diff --git a/packages/contact-center/user-state/package.json b/packages/contact-center/user-state/package.json index bbc117e8a..b78bbce93 100644 --- a/packages/contact-center/user-state/package.json +++ b/packages/contact-center/user-state/package.json @@ -61,4 +61,4 @@ "react": ">=18.3.1", "react-dom": ">=18.3.1" } -} \ No newline at end of file +} diff --git a/playwright/UserStateTest.spec.ts b/playwright/UserStateTest.spec.ts new file mode 100644 index 000000000..ad91ab2ab --- /dev/null +++ b/playwright/UserStateTest.spec.ts @@ -0,0 +1,585 @@ +import { test, expect, Page, BrowserContext } from '@playwright/test'; +import { oauthLogin, multiLoginEnable, initialisePage } from './Utils/initUtils'; +import { desktopLogin, extensionLogin, dialLogin, stationLogout } from './Utils/stationLoginUtils'; +import { getCurrentState, changestate, verifyCurrentState, getStateElapsedTime, checkConsole, checkCallbackSequence } from './Utils/stateUtils'; + +import dotenv from 'dotenv'; +dotenv.config(); + + +// ===================================================================================================== +// TEST SUITE 1: Testing basic functionality of the User State widget (All Three Types of Station Login) +// ===================================================================================================== + +test.describe('๐Ÿ” User State Widget Functionality Suite', () => { + + test('should successfully login via BROWSER mode', async ({ page }) => { + const consoleMessages: string[] = []; + + page.on('console', msg => { + const message = msg.text(); + consoleMessages.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Important Browser Console:', message); + } + }); + + console.log('\n=== ๐Ÿ–ฅ๏ธ BROWSER LOGIN TEST STARTED ==='); + + await oauthLogin(page); + await multiLoginEnable(page); + + await initialisePage(page); + + const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); + if (loginButtonExists) { + await desktopLogin(page); + } else { + await stationLogout(page); + await desktopLogin(page); + } + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… Browser Login Successful!'); + + // Test state change and API calls + console.log('๐Ÿ”„ Testing state change functionality...'); + consoleMessages.length = 0; + + await changestate(page, 'Available'); + await page.waitForTimeout(3000); + + const isCallbackSuccessful = await checkConsole(page, 'Available', consoleMessages); + console.log(`๐Ÿ“Š State Change Result: ${isCallbackSuccessful ? 'PASSED โœ…' : 'FAILED โŒ'}`); + expect(isCallbackSuccessful).toBe(true); + + await verifyCurrentState(page, 'Available'); + const elapsedTime = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Timer reset confirmed - Elapsed time: ${elapsedTime}`); + console.log('=== BROWSER LOGIN TEST COMPLETED ===\n'); + }); + + /*test('should successfully login via EXTENSION mode', async ({ page }) => { + const consoleMessages: string[] = []; + + page.on('console', msg => { + const message = msg.text(); + consoleMessages.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Important Browser Console:', message); + } + }); + + console.log('\n=== ๐Ÿ”Œ EXTENSION LOGIN TEST STARTED ==='); + + await oauthLogin(page); + await initialisePage(page); + await page.getByTestId('station-login-widget').waitFor({ state: 'visible' }); + + const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); + if (loginButtonExists) { + await extensionLogin(page); + } else { + await stationLogout(page); + await extensionLogin(page); + } + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… Extension Login Successful!'); + + // Test state change and API calls + console.log('๐Ÿ”„ Testing state change functionality...'); + consoleMessages.length = 0; + + await changestate(page, 'Available'); + await page.waitForTimeout(3000); + + const isCallbackSuccessful = await checkConsole(page, 'Available', consoleMessages); + console.log(`๐Ÿ“Š State Change Result: ${isCallbackSuccessful ? 'PASSED โœ…' : 'FAILED โŒ'}`); + expect(isCallbackSuccessful).toBe(true); + + await verifyCurrentState(page, 'Available'); + const elapsedTime = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Timer reset confirmed - Elapsed time: ${elapsedTime}`); + console.log('=== EXTENSION LOGIN TEST COMPLETED ===\n'); + }); + + test('should successfully login via AGENT_DN mode', async ({ page }) => { + const consoleMessages: string[] = []; + + page.on('console', msg => { + const message = msg.text(); + consoleMessages.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Important Browser Console:', message); + } + }); + + console.log('\n=== ๐Ÿ“ž AGENT_DN LOGIN TEST STARTED ==='); + + await oauthLogin(page); + await initialisePage(page); + await page.getByTestId('station-login-widget').waitFor({ state: 'visible' }); + + const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); + if (loginButtonExists) { + await dialLogin(page); + } else { + await stationLogout(page); + await dialLogin(page); + } + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… Agent DN Login Successful!'); + + // Test state change and API calls + console.log('๐Ÿ”„ Testing state change functionality...'); + consoleMessages.length = 0; + + await changestate(page, 'Available'); + await page.waitForTimeout(3000); + + const isCallbackSuccessful = await checkConsole(page, 'Available', consoleMessages); + console.log(`๐Ÿ“Š State Change Result: ${isCallbackSuccessful ? 'PASSED โœ…' : 'FAILED โŒ'}`); + expect(isCallbackSuccessful).toBe(true); + + await verifyCurrentState(page, 'Available'); + const elapsedTime = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Timer reset confirmed - Elapsed time: ${elapsedTime}`); + console.log('=== AGENT_DN LOGIN TEST COMPLETED ===\n'); + });*/ +}); + +// ================================================================================= +// TEST SUITE 2: Test to check if the User State and the timer is retained on refreshing the page +// ================================================================================= + +test.describe('๐Ÿ“ฑ User State retention and timer check on page refresh', () => { + + test('should init SDK, login station and trigger onStateChange callback on page refresh', async ({ page, context }) => { + const consoleMessages: string[] = []; + + page.on('console', msg => { + const message = msg.text(); + consoleMessages.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Important Browser Console:', message); + } + }); + + console.log('\n=== ๐Ÿ“ฑ STATION LOGIN & CALLBACK TEST STARTED ==='); + + // Initial login + /* await oauthLogin(page); + await initialisePage(page);*/ + + const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); + if (loginButtonExists) { + await extensionLogin(page); + } else { + await stationLogout(page); + await extensionLogin(page); + } + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… Initial Station Login Successful!'); + + // Set a known state + await changestate(page, 'Available'); + await page.waitForTimeout(2000); + console.log('๐Ÿ“Š State set to Available'); + + // Wait and get timer value for comparison + await page.waitForTimeout(3000); + const initialTimer = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Initial timer value: ${initialTimer}`); + + // Clear console messages and refresh page + consoleMessages.length = 0; + console.log('๐Ÿ”„ Refreshing page to test onStateChange callback...'); + await page.reload(); + + // Re-initialize after refresh + await initialisePage(page); + await page.waitForTimeout(5000); // Wait for SDK init and callback + + // Verify SDK init and station login + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… SDK initialized and station logged in after refresh'); + + // Check if onStateChange callback was triggered with existing state + const callbackTriggered = await checkConsole(page, 'Available', consoleMessages); + console.log(`๐Ÿ“ž onStateChange callback triggered: ${callbackTriggered ? 'YES โœ…' : 'NO โŒ'}`); + expect(callbackTriggered).toBe(true); + + // Verify current state + await verifyCurrentState(page, 'Available'); + + // Check timer continuity (should match Agent Desktop time) + + + const refreshedTimer = await getStateElapsedTime(page); + console.log('โฑ๏ธ Timer after refresh on the sample app:', refreshedTimer); + + console.log('=== STATION LOGIN & CALLBACK TEST COMPLETED ===\n'); + }); +}); + +// =================================================================== +// TEST SUITE 3: State Change to Available & Theme Validation +// =================================================================== + +test.describe('๐ŸŸข State Change to Available & Theme Validation Suite', () => { + + test('should change state to Available with proper theme and callback sequence', async ({ page }) => { + const consoleMessages: string[] = []; + + page.on('console', msg => { + const message = msg.text(); + consoleMessages.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Important Browser Console:', message); + } + }); + + console.log('\n=== ๐ŸŸข STATE CHANGE TO AVAILABLE & THEME TEST STARTED ==='); + + // Login via telephony mode + await oauthLogin(page); + await initialisePage(page); + + const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); + if (loginButtonExists) { + await extensionLogin(page); + } else { + await stationLogout(page); + await extensionLogin(page); + } + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… SDK initialized and station logged in'); + + // Set initial state (not Available) + await changestate(page, 'Meeting'); + await page.waitForTimeout(2000); + console.log('๐Ÿ“Š Initial state set to Meeting'); + + // Clear console messages before changing to Available + consoleMessages.length = 0; + console.log('๐Ÿ”„ Changing state to Available...'); + + // Change to Available state + await changestate(page, 'Available'); + await page.waitForTimeout(3000); + + // Verify state change + await verifyCurrentState(page, 'Available'); + console.log('โœ… State successfully changed to Available'); + + // Verify onStateChange callback was called AFTER API success + + const sequenceValid = await checkCallbackSequence(page, 'Available', consoleMessages); + console.log(`๐Ÿ“ž onStateChange called AFTER API success: ${sequenceValid ? 'YES โœ…' : 'NO โŒ'}`); + expect(sequenceValid).toBe(true); + + // Check theme appearance (Green for Available) + // Note: This would need to be implemented based on your theme system + console.log('๐ŸŽจ Verifying Green theme for Available state...'); + // Add theme verification logic here based on your implementation + const themeElement = await page.getByTestId('state-select'); // Replace with actual test ID or selector + const themeColor = await themeElement.evaluate(el => getComputedStyle(el).backgroundColor); + if (themeColor === 'rgb(206, 245, 235)') { // Green theme with proper spacing + console.log('โœ… Green theme confirmed for Available state'); + } else { + console.log(`โŒ Theme color mismatch: expected rgb(206, 245, 235), got ${themeColor}`); + } + + // Test Grey theme for other states + console.log('๐Ÿ”„ Testing Grey theme for non-Available states...'); + + consoleMessages.length = 0; + + await changestate(page, 'Meeting'); + await page.waitForTimeout(2000); + + await verifyCurrentState(page, 'Meeting'); + console.log('๐ŸŽจ Verifying Grey theme for Meeting state...'); + // Add grey theme verification logic here + const meetingThemeElement = await page.getByTestId('state-select'); // Replace with actual test ID or selector + const meetingThemeColor = await meetingThemeElement.evaluate(el => getComputedStyle(el).backgroundColor); + if (meetingThemeColor === 'rgba(0, 0, 0, 0.11)') { // Grey theme with proper spacing + console.log('โœ… Grey theme confirmed for Meeting state'); + } else { + console.log(`โŒ Theme color mismatch: expected rgba(0, 0, 0, 0.11), got ${meetingThemeColor}`); + } + + console.log('=== STATE CHANGE TO AVAILABLE & THEME TEST COMPLETED ===\n'); + }); +}); + +// =================================================================== +// TEST SUITE 4: Idle Code State Transitions & Dual Timer Validation +// =================================================================== + +test.describe('โฐ Idle Code State Transitions & Dual Timer Suite', () => { + + test('should transition from Meeting to Lunch Break with dual timer display', async ({ page }) => { + const consoleMessages: string[] = []; + + page.on('console', msg => { + const message = msg.text(); + consoleMessages.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Important Browser Console:', message); + } + }); + + console.log('\n=== โฐ IDLE CODE STATE TRANSITIONS & DUAL TIMER TEST STARTED ==='); + + // Login and ensure SDK init + /* await oauthLogin(page); + await initialisePage(page);*/ + + const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); + if (loginButtonExists) { + await extensionLogin(page); + } else { + await stationLogout(page); + await extensionLogin(page); + } + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… SDK initialized and station logged in'); + + await verifyCurrentState(page, 'Meeting'); + + // Wait to build up some idle time + console.log('โณ Building up idle time...'); + await page.waitForTimeout(2000); + + const meetingTimer = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Meeting state timer: ${meetingTimer}`); + + // Clear console and change to Lunch Break + consoleMessages.length = 0; + console.log('๐Ÿ”„ Changing from Meeting to Lunch...'); + + await changestate(page, 'Lunch'); + await page.waitForTimeout(3000); + + // Verify state change + await verifyCurrentState(page, 'Lunch'); + console.log('โœ… State successfully changed to Lunch '); + + // Check for dual timer display + console.log('โฐ Verifying dual timer display...'); + const lunchTimer = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Lunch state timer: ${lunchTimer}`); + + // Note: Add logic here to verify dual timer display + // - One timer for total idle time + // - One timer for current state (Lunch) time + console.log('โœ… Dual timer confirmed:'); + + // Verify onStateChange callback with Lunch Idle Code + const lunchCallbackSuccess = await checkConsole(page, 'Lunch', consoleMessages); + console.log(`๐Ÿ“ž onStateChange callback for Lunch : ${lunchCallbackSuccess ? 'SUCCESS โœ…' : 'FAILED โŒ'}`); + expect(lunchCallbackSuccess).toBe(true); + + console.log('=== IDLE CODE STATE TRANSITIONS & DUAL TIMER TEST COMPLETED ===\n'); + }); +}); + +// =================================================================== +// TEST SUITE 5: Cross-Page State & Timer Synchronization +// =================================================================== + +test.describe('๐Ÿ”„ Cross-Page State & Timer Synchronization Suite', () => { + + test('should synchronize state changes and timers across multiple pages', async ({ page, context }) => { + const consoleMessagesPage1: string[] = []; + const consoleMessagesPage2: string[] = []; + + // Setup console listeners for page 1 + page.on('console', msg => { + const message = msg.text(); + consoleMessagesPage1.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Page 1 Console:', message); + } + }); + + console.log('\n=== ๐Ÿ”„ CROSS-PAGE SYNCHRONIZATION TEST STARTED ==='); + + // ===== PAGE 1 SETUP ===== + console.log('๐Ÿ“ฑ Setting up Page 1...'); + /* await oauthLogin(page); + await multiLoginEnable(page); + await initialisePage(page);*/ + + const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); + if (loginButtonExists) { + await extensionLogin(page); + } else { + await stationLogout(page); + await extensionLogin(page); + } + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… Page 1: Extension Login Successful!'); + + // Set initial state on Page 1 + await page.waitForTimeout(2000); + await verifyCurrentState(page, 'Meeting'); + const page1InitialTimer = await getStateElapsedTime(page); + console.log(`๐Ÿ“Š Page 1: Initial state set to Meeting, Timer: ${page1InitialTimer}`); + + // ===== PAGE 2 SETUP ===== + console.log('\n๐Ÿ“ฑ Setting up Page 2...'); + const page2 = await context.newPage(); + + // Setup console listeners for page 2 + page2.on('console', msg => { + const message = msg.text(); + consoleMessagesPage2.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Page 2 Console:', message); + } + }); + + await oauthLogin(page2); + await initialisePage(page2); + await page2.waitForTimeout(3000); // Wait for SDK initialization + + await expect(page2.getByTestId('state-select')).toBeVisible(); + console.log('โœ… Page 2: SDK initialized and widgets loaded!'); + + // Verify Page 2 shows the same state as Page 1 + const page2InitialState = await getCurrentState(page2); + const page2InitialTimer = await getStateElapsedTime(page2); + console.log(`๐Ÿ“Š Page 2: Synchronized state - ${page2InitialState}, Timer: ${page2InitialTimer}`); + + if (page2InitialState === 'Meeting') { + console.log('โœ… Initial state synchronization confirmed across pages'); + } else { + console.log(`โŒ Initial state sync failed - Page 1: Meeting, Page 2: ${page2InitialState}`); + } + + // ===== TEST 1: STATE CHANGE SYNCHRONIZATION ===== + console.log('\n๐Ÿ”„ Testing state change synchronization...'); + console.log('๐Ÿ“ Changing state to Available on Page 1...'); + + // Clear console messages before state change + consoleMessagesPage1.length = 0; + consoleMessagesPage2.length = 0; + + // Change state on Page 1 + await changestate(page, 'Available'); + await page.waitForTimeout(3000); + + // Verify state change on Page 1 + await verifyCurrentState(page, 'Available'); + const page1NewTimer = await getStateElapsedTime(page); + console.log(`๐Ÿ“Š Page 1: State changed to Available, Timer reset: ${page1NewTimer}`); + + // Wait for synchronization and check Page 2 + await page2.waitForTimeout(5000); // Allow time for sync + const page2SyncState = await getCurrentState(page2); + const page2SyncTimer = await getStateElapsedTime(page2); + console.log(`๐Ÿ“Š Page 2: Synchronized state - ${page2SyncState}, Timer: ${page2SyncTimer}`); + + // Verify synchronization + if (page2SyncState === 'Available') { + console.log('โœ… State change synchronization: SUCCESS'); + } else { + console.log(`โŒ State change synchronization: FAILED - Expected Available, got ${page2SyncState}`); + } + + // Verify onStateChange callbacks on both pages + const page1Callback = await checkConsole(page, 'Available', consoleMessagesPage1); + const page2Callback = await checkConsole(page2, 'Available', consoleMessagesPage2); + + console.log(`๐Ÿ“ž Page 1 onStateChange callback: ${page1Callback ? 'SUCCESS โœ…' : 'FAILED โŒ'}`); + console.log(`๐Ÿ“ž Page 2 onStateChange callback: ${page2Callback ? 'SUCCESS โœ…' : 'FAILED โŒ'}`); + + expect(page1Callback).toBe(true); + expect(page2Callback).toBe(true); + + // ===== TEST 2: TIMER SYNCHRONIZATION ===== + console.log('\nโฑ๏ธ Testing timer synchronization...'); + + // Wait and compare timers on both pages + await page.waitForTimeout(5000); + + const finalPage1Timer = await getStateElapsedTime(page); + const finalPage2Timer = await getStateElapsedTime(page2); + + console.log(`โฑ๏ธ Final Timer Comparison:`); + console.log(` Page 1: ${finalPage1Timer}`); + console.log(` Page 2: ${finalPage2Timer}`); + + // Parse timer values for comparison (assuming format like "00:05" or "05:32") + const parseTimer = (timer: string) => { + const parts = timer.split(':'); + return parseInt(parts[0]) * 60 + parseInt(parts[1]); + }; + + try { + const timer1Seconds = parseTimer(finalPage1Timer); + const timer2Seconds = parseTimer(finalPage2Timer); + const timeDiff = Math.abs(timer1Seconds - timer2Seconds); + + if (timeDiff == 0) { + console.log('โœ… Timer synchronization: SUCCESS'); + } else { + console.log(`โŒ Timer synchronization: FAILED (${timeDiff} seconds difference)`); + } + } catch (error) { + console.log('โš ๏ธ Timer comparison skipped - unable to parse timer format'); + } + + + + // ===== CLEANUP ===== + console.log('\n๐Ÿงน Cleaning up...'); + await page2.close(); + console.log('โœ… Page 2 closed'); + + console.log('=== CROSS-PAGE SYNCHRONIZATION TEST COMPLETED ===\n'); + }); + + +}); + diff --git a/playwright/UserStateWidgetTest.spec.ts b/playwright/UserStateWidgetTest.spec.ts new file mode 100644 index 000000000..6f8e63add --- /dev/null +++ b/playwright/UserStateWidgetTest.spec.ts @@ -0,0 +1,241 @@ +import { test, expect, Page, BrowserContext } from '@playwright/test'; +import { oauthLogin, multiLoginEnable, initialisePage } from './Utils/initUtils'; +import { desktopLogin, extensionLogin, dialLogin, stationLogout } from './Utils/stationLoginUtils'; +import { getCurrentState, changestate, verifyCurrentState, getStateElapsedTime, checkConsole, checkCallbackSequence } from './Utils/stateUtils'; + +import dotenv from 'dotenv'; + +dotenv.config(); + +test.describe('๐Ÿ” User State Widget Functionality Suite', () => { + test('User state widget should function as expected', async ({ page, context }) => { + const consoleMessages: string[] = []; + + // ======================================================================== + // ๐Ÿ“ฑ CONSOLE MESSAGE LISTENER SETUP + // ======================================================================== + page.on('console', msg => { + const message = msg.text(); + consoleMessages.push(message); + + if (message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.includes('onStateChangeInvoked') || + message.includes('onStateChange invoked')) { + console.log('๐Ÿ” Important Browser Console:', message); + } + }); + + // ======================================================================== + // ๐Ÿš€ PHASE 1: INITIAL AUTHENTICATION & SETUP + // ======================================================================== + console.log('\n' + '='.repeat(80)); + console.log('๐Ÿš€ USER STATE WIDGET FUNCTIONALITY TEST STARTED'); + console.log('='.repeat(80)); + + console.log('\n๐Ÿ“‹ Phase 1: Initial Authentication & Setup'); + console.log('โ”€'.repeat(50)); + console.log('๐Ÿ”‘ Initializing OAuth authentication...'); + await oauthLogin(page); + + console.log('๐Ÿ”„ Enabling multi-login capabilities...'); + await multiLoginEnable(page); + + console.log('โš™๏ธ Initializing widget components...'); + await initialisePage(page); + + // Station Login Logic + const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); + if (loginButtonExists) { + console.log('๐Ÿ”Œ Performing extension login...'); + await extensionLogin(page); + } else { + console.log('๐Ÿ”Œ Performing station logout & extension login...'); + await stationLogout(page); + await extensionLogin(page); + } + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… Station login completed successfully!\n'); + + // ======================================================================== + // ๐ŸŽจ PHASE 2: INITIAL STATE & THEME VERIFICATION + // ======================================================================== + console.log('๐Ÿ“‹ Phase 2: Initial State & Theme Verification'); + console.log('โ”€'.repeat(50)); + + consoleMessages.length = 0; + await verifyCurrentState(page, 'Meeting'); + + console.log('๐ŸŽจ Analyzing theme for Meeting state...'); + const meetingThemeElement = await page.getByTestId('state-select'); + const meetingThemeColor = await meetingThemeElement.evaluate(el => getComputedStyle(el).backgroundColor); + + if (meetingThemeColor === 'rgba(0, 0, 0, 0.11)') { + console.log('โœ… Grey theme confirmed for Meeting state'); + } else { + console.log(`โš ๏ธ Theme Analysis: Expected rgba(0, 0, 0, 0.11), detected ${meetingThemeColor}`); + } + + await page.waitForTimeout(5000); + const initialTimer = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Current timer value: ${initialTimer}\n`); + + // ======================================================================== + // ๐Ÿ”„ PHASE 3: STATE CHANGE & THEME VALIDATION + // ======================================================================== + console.log('๐Ÿ“‹ Phase 3: State Change & Theme Validation'); + console.log('โ”€'.repeat(50)); + + console.log('๐Ÿ”„ Initiating state change: Meeting โ†’ Available...'); + await changestate(page, 'Available'); + await page.waitForTimeout(3000); + + console.log('๐ŸŽจ Analyzing theme for Available state...'); + const themeElement = await page.getByTestId('state-select'); + const themeColor = await themeElement.evaluate(el => getComputedStyle(el).backgroundColor); + + if (themeColor === 'rgb(206, 245, 235)') { + console.log('โœ… Green theme confirmed for Available state'); + } else { + console.log(`โš ๏ธ Theme Analysis: Expected rgb(206, 245, 235), detected ${themeColor}`); + } + + const isCallbackSuccessful = await checkConsole(page, 'Available', consoleMessages); + console.log(`๐Ÿ“Š State Change Validation: ${isCallbackSuccessful ? 'โœ… PASSED' : 'โŒ FAILED'}`); + expect(isCallbackSuccessful).toBe(true); + + await verifyCurrentState(page, 'Available'); + const elapsedTime = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Post-change elapsed time: ${elapsedTime}`); + console.log('๐ŸŽฏ Timer reset confirmation completed!\n'); + + // ======================================================================== + // ๐Ÿ”„ PHASE 4: PAGE REFRESH & STATE PERSISTENCE TESTING + // ======================================================================== + console.log('๐Ÿ“‹ Phase 4: Page Refresh & State Persistence Testing'); + console.log('โ”€'.repeat(50)); + + consoleMessages.length = 0; + console.log('๐Ÿ”„ Performing page refresh to test state persistence...'); + await page.reload(); + + console.log('โš™๏ธ Re-initializing components after refresh...'); + await initialisePage(page); + await page.waitForTimeout(5000); + + console.log('๐Ÿ–ฅ๏ธ Opening Agent Desktop for timer synchronization test...'); + const agentDeskPage = await context.newPage(); + + await expect(page.getByTestId('state-select')).toBeVisible(); + console.log('โœ… SDK re-initialization completed after refresh'); + + const callbackTriggered = await checkConsole(page, 'Available', consoleMessages); + console.log(`๐Ÿ“ž onStateChange callback status: ${callbackTriggered ? 'โœ… TRIGGERED' : 'โŒ NOT TRIGGERED'}`); + expect(callbackTriggered).toBe(true); + + await verifyCurrentState(page, 'Available'); + console.log('โœ… State persistence verified after refresh'); + + + const sampleAppTimer = await getStateElapsedTime(page); + + + + + + // ======================================================================== + // ๐Ÿ” PHASE 5: API CALLBACK SEQUENCE VALIDATION + // ======================================================================== + console.log('๐Ÿ“‹ Phase 5: API Callback Sequence Validation'); + console.log('โ”€'.repeat(50)); + + console.log('๐Ÿ”„ Testing callback sequence order...'); + await changestate(page, 'Meeting'); + await changestate(page, 'Available'); + await verifyCurrentState(page, 'Available'); + console.log('โœ… State transition completed successfully'); + + const sequenceValid = await checkCallbackSequence(page, 'Available', consoleMessages); + console.log(`๐Ÿ“ž Callback sequence validation: ${sequenceValid ? 'โœ… CORRECT ORDER' : 'โŒ INCORRECT ORDER'}`); + expect(sequenceValid).toBe(true); + console.log('๐ŸŽฏ API callback sequence verification completed\n'); + + // ======================================================================== + // ๐ŸŒ PHASE 6: MULTI-SESSION SYNCHRONIZATION TESTING + // ======================================================================== + console.log('๐Ÿ“‹ Phase 6: Multi-Session Synchronization Testing'); + console.log('โ”€'.repeat(50)); + + console.log('๐ŸŒ Initializing multi-session environment...'); + const multiSessionPage = await context.newPage(); + await oauthLogin(multiSessionPage); + await initialisePage(multiSessionPage); + await multiSessionPage.waitForTimeout(3000); + + console.log('๐Ÿ”„ Testing state synchronization across sessions...'); + await changestate(page, 'Available'); + await verifyCurrentState(page, 'Available'); + await multiSessionPage.waitForTimeout(2000); + console.log('โœ… State change executed in primary session'); + + console.log('๐Ÿ” Verifying synchronization in secondary session...'); + await verifyCurrentState(multiSessionPage, 'Available'); + console.log('โœ… State synchronization confirmed across sessions'); + + // Timer Synchronization Check + await multiSessionPage.waitForTimeout(2000); + const timer1 = await getStateElapsedTime(page); + const timer2 = await getStateElapsedTime(multiSessionPage); + + console.log(`โฑ๏ธ Primary Session Timer: ${timer1}`); + console.log(`โฑ๏ธ Secondary Session Timer: ${timer2}`); + + if (timer1 === timer2) { + console.log('๐ŸŽฏ Multi-session timer synchronization verified'); + } else { + console.log('โŒ Multi-session timer synchronization failed'); + } + + await multiSessionPage.close(); + console.log('๐Ÿ”’ Multi-session environment closed\n'); + + // ======================================================================== + // โฐ PHASE 7: IDLE STATE TRANSITION & DUAL TIMER TESTING + // ======================================================================== + console.log('๐Ÿ“‹ Phase 7: Idle State Transition & Dual Timer Testing'); + console.log('โ”€'.repeat(50)); + + console.log('๐Ÿ”„ Testing idle-to-idle state transitions...'); + await changestate(page, 'Meeting'); + await verifyCurrentState(page, 'Meeting'); + await page.waitForTimeout(2000); + + console.log('๐Ÿ”„ Executing transition: Meeting โ†’ Lunch...'); + await changestate(page, 'Lunch'); + await verifyCurrentState(page, 'Lunch'); + console.log('โœ… Idle state transition completed successfully'); + + const isLunchCallbackSuccessful = await checkConsole(page, 'Lunch', consoleMessages); + console.log(`๐Ÿ“Š Lunch State Callback Validation: ${isLunchCallbackSuccessful ? 'โœ… PASSED' : 'โŒ FAILED'}`); + expect(isLunchCallbackSuccessful).toBe(true); + + await page.waitForTimeout(5000); + const finalTimer = await getStateElapsedTime(page); + console.log(`โฑ๏ธ Dual timer after Lunch transition: ${finalTimer}`); + + // ======================================================================== + // ๐Ÿ TEST COMPLETION SUMMARY + // ======================================================================== + console.log('\n' + '='.repeat(80)); + console.log('๐Ÿ USER STATE WIDGET FUNCTIONALITY TEST COMPLETED SUCCESSFULLY'); + console.log(' โœ… Authentication & Setup'); + console.log(' โœ… Theme Verification'); + console.log(' โœ… State Change & Validation'); + console.log(' โœ… Page Refresh & Persistence'); + console.log(' โœ… Callback Sequence Validation'); + console.log(' โœ… Multi-Session Synchronization'); + console.log(' โœ… Idle State Transitions'); + console.log('='.repeat(80) + '\n'); + }); +}); \ No newline at end of file diff --git a/playwright/Utils/initUtils.ts b/playwright/Utils/initUtils.ts new file mode 100644 index 000000000..66044049a --- /dev/null +++ b/playwright/Utils/initUtils.ts @@ -0,0 +1,44 @@ +import { Page,expect } from '@playwright/test'; +import dotenv from 'dotenv'; + +dotenv.config(); + + +export const oauthLogin = async (page: Page): Promise => { + if (!process.env.PLAYWRIGHT_USERNAME || !process.env.PLAYWRIGHT_PASSWORD) { + throw new Error('PLAYWRIGHT_USERNAME and PLAYWRIGHT_PASSWORD must be set in the environment variables'); + } + + await page.goto('http://localhost:3000'); + await page.locator('#select-base-triggerid').getByText('Access Token').click(); + await page.getByTestId('samples:login_option_oauth').getByText('Login with Webex').click(); + await page.getByRole('button', { name: 'Login with Webex' }).click(); + await page.getByRole('textbox', { name: 'name@example.com' }).fill(process.env.PLAYWRIGHT_USERNAME); + await page.getByRole('link', { name: 'Sign in' }).click(); + await page.waitForTimeout(3000); + // Check if Init Widgets button is visible after username sign in (Multi session) + const initWidgetsButton = page.getByRole('button', { name: 'Init Widgets' }); + const isInitWidgetsVisible = await initWidgetsButton.isVisible().catch(() => false); + + if (!isInitWidgetsVisible) { + // If Init Widgets button is not visible, proceed with password entry + await page.getByRole('textbox', { name: 'Password' }).fill(process.env.PLAYWRIGHT_PASSWORD); + await page.getByRole('button', { name: 'Sign in' }).click(); + } + + await page.getByRole('checkbox', { name: 'Show Agent Profile' }).check(); + +} + +export const multiLoginEnable = async (page: Page): Promise => { + await page.getByText('Enable Multi Login').click(); +} + +export const initialisePage = async (page: Page): Promise => { + console.log('Initialising widgets...'); + await page.getByRole('button', { name: 'Init Widgets' }).click(); + await page.getByRole('button', { name: 'Init Widgets' }).click(); + + await page.getByTestId('station-login-widget').waitFor({ state: 'visible' }); + console.log('Widgets initialised successfully!'); +}; \ No newline at end of file diff --git a/playwright/Utils/stateUtils.ts b/playwright/Utils/stateUtils.ts new file mode 100644 index 000000000..f7a259800 --- /dev/null +++ b/playwright/Utils/stateUtils.ts @@ -0,0 +1,174 @@ +import { Page,expect } from '@playwright/test'; +import dotenv from 'dotenv'; + +dotenv.config(); + + +export const changestate = async (page: Page, userState: string): Promise => { + console.log(`Changing state to "${userState}"...`); + + // Get the current state name + const currentState = await page.getByTestId('state-select').getByTestId('state-name').innerText(); + if (currentState.trim() === userState) { + console.log(`Already in state "${userState}". No change needed.`); + return; + } + + // Check if the desired state exists in the dropdown + await page.getByTestId('state-select').click(); + const stateItem = page.getByTestId(`state-item-${userState}`); + const isValidState = await stateItem.isVisible().catch(() => false); + + if (!isValidState) { + throw new Error(`State "${userState}" is not a valid state option.`); + } + + // Change to the desired state + await stateItem.click(); + await expect(page.getByTestId('state-select').getByTestId('state-name')).toContainText(userState); + console.log(`State changed to "${userState}".`); +}; + +export const getCurrentState = async (page: Page): Promise => { + const stateName = await page.getByTestId('state-select').getByTestId('state-name').innerText(); + return stateName.trim(); +}; + +export const verifyCurrentState = async (page: Page, expectedState: string): Promise => { + const currentState = await getCurrentState(page); + if (currentState !== expectedState) { + throw new Error(`Expected state "${expectedState}" but found "${currentState}".`); + } + console.log(`Current state is verified as "${expectedState}".`); +}; + +export const getStateElapsedTime = async (page: Page): Promise => { + // Directly select the timer by its test id + const timerText = await page.getByTestId('elapsed-time').innerText(); + return timerText.trim(); +}; + +// Check console for onStateChange callback and state detection +export const checkConsole = async (page: Page, state: string, consoleMessages: string[]): Promise => { + console.log(`Checking console for onStateChange callback with state: ${state}`); + + // Check for onStateChange invoked message + const onStateChangeInvoked = consoleMessages.some(message => + message.toLowerCase().includes('onstatechange') && message.toLowerCase().includes('invoked') + ); + + // Check for API call success messages + const apiCallSuccess = consoleMessages.some(message => + message.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('Submit event: WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + message.includes('AGENT_STATE_CHANGE_SUCCESS') || + message.toLowerCase().includes('state change success') || + message.toLowerCase().includes('state changed successfully') + ); + + // Since the object properties might be hidden, we need to look for different patterns + const stateLower = state.toLowerCase(); + const stateFound = consoleMessages.some(message => { + const messageLower = message.toLowerCase(); + return messageLower.includes(stateLower) || + // Look for object indicators that might contain the state + (messageLower.includes('object') && messageLower.includes('target')) || + // Look for proxy objects + messageLower.includes('proxy') || + // Look for any mention of the state even if properties are hidden + messageLower.includes(`${stateLower}`) || + // Check for common object notation patterns + messageLower.includes(`"name":"${stateLower}"`) || + messageLower.includes(`name: "${stateLower}"`) || + messageLower.includes(`'name':'${stateLower}'`) || + messageLower.includes(`state: "${stateLower}"`) || + messageLower.includes(`"state":"${stateLower}"`) || + // Check for collapsed object indicators + messageLower.includes('{โ€ฆ}') || + messageLower.includes('[object object]'); + }); + + // Alternative approach: Check if we can evaluate the console objects directly + let stateFoundInExpandedObject = false; + + try { + // Try to access the actual state from the page's console context + stateFoundInExpandedObject = await page.evaluate((targetState) => { + // Check if there are any recent state change events stored in window + if ((window as any).lastStateChangeEvent) { + const event = (window as any).lastStateChangeEvent; + return event.name && event.name.toLowerCase() === targetState.toLowerCase(); + } + return false; + }, state); + } catch (error) { + console.log('Could not evaluate state from page context'); + } + + // Result now includes API call success check + const result = onStateChangeInvoked && (stateFound || stateFoundInExpandedObject) && apiCallSuccess; + + if (result) { + console.log(`\nโœ… SUCCESS: onStateChange invoked, ${state} state detected, and API call successful\n`); + } else { + console.log(`\nโŒ FAILED: onStateChange with ${state} state verification failed`); + console.log(` - onStateChange invoked: ${onStateChangeInvoked}`); + console.log(` - ${state} state found in messages: ${stateFound}`); + console.log(` - ${state} state found in expanded object: ${stateFoundInExpandedObject}`); + console.log(` - API call success detected: ${apiCallSuccess}`); + + // Show only relevant console messages for debugging + console.log('\n๐Ÿ” Relevant console messages:'); + const relevantMessages = consoleMessages.filter(msg => + msg.toLowerCase().includes('onstatechange') || + msg.toLowerCase().includes(state.toLowerCase()) || + msg.toLowerCase().includes('state') || + msg.includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') + ); + + if (relevantMessages.length > 0) { + relevantMessages.forEach((msg, index) => { + console.log(` ${index + 1}: ${msg}`); + }); + } else { + console.log(' No relevant messages found'); + } + console.log(''); + } + + return result; +}; + +export async function checkCallbackSequence(page: Page, expectedState: string, consoleMessages: string[]): Promise { + let apiSuccessIndex = -1; + let callbackIndex = -1; + + + // Find the index of API success message + for (let i = 0; i < consoleMessages.length; i++) { + if (consoleMessages[i].includes('WXCC_SDK_AGENT_STATE_CHANGE_SUCCESS') || + consoleMessages[i].includes('AGENT_STATE_CHANGE_SUCCESS')) { + apiSuccessIndex = i; + break; + } + } + + // Find the index of onStateChange callback + for (let i = 0; i < consoleMessages.length; i++) { + if (consoleMessages[i].includes('onStateChangeInvoked') || + consoleMessages[i].includes('onStateChange invoked')) { + callbackIndex = i; + break; + } + } + + // Verify both events occurred and callback came after API success + if (apiSuccessIndex !== -1 && callbackIndex !== -1) { + console.log(`๐Ÿ” API Success at index: ${apiSuccessIndex}, Callback at index: ${callbackIndex}`); + return callbackIndex > apiSuccessIndex; + } + + console.log(`โŒ Missing events - API Success: ${apiSuccessIndex !== -1}, Callback: ${callbackIndex !== -1}`); + return false; +} + diff --git a/playwright/Utils/stationLoginUtils.ts b/playwright/Utils/stationLoginUtils.ts new file mode 100644 index 000000000..bfd5e3742 --- /dev/null +++ b/playwright/Utils/stationLoginUtils.ts @@ -0,0 +1,68 @@ +import { Page } from '@playwright/test'; +import dotenv from 'dotenv'; + +dotenv.config(); + +/** + * Perform desktop login. + * @param page - The Playwright page object. + */ +export const desktopLogin = async (page: Page): Promise => { + console.log('Performing Desktop Login...'); + await page.getByTestId('login-option-select').locator('#select-base-triggerid svg').click(); + await page.getByTestId('login-option-Desktop').click(); + await page.getByTestId('teams-dropdown-select').locator('#select-base-triggerid div').click(); + await page.waitForTimeout(200); + await page.locator('[data-testid^="teams-dropdown-"]').nth(1).locator('span, div').first().click(); + await page.waitForTimeout(200); + + await page.getByTestId('login-button').click(); +}; + +/** + * Perform extension login. + * @param page - The Playwright page object. + */ +export const extensionLogin = async (page: Page): Promise => { + const extensionNumber = process.env.EXTENSION_NUMBER; + if (!extensionNumber) { + throw new Error('EXTENSION_NUMBER is not defined in the .env file'); + } + + console.log('Performing Extension Login...'); + await page.getByTestId('login-option-select').locator('#select-base-triggerid svg').click(); + await page.getByTestId('login-option-Extension').click(); + await page.getByRole('textbox', { name: 'Extension' }).fill(extensionNumber); + await page.getByTestId('teams-dropdown-select').locator('#select-base-triggerid div').click(); + await page.waitForTimeout(200); + await page.locator('[data-testid^="teams-dropdown-"]').nth(1).locator('span, div').first().click(); + await page.getByTestId('login-button').click(); +}; + +/** + * Perform dial login. + * @param page - The Playwright page object. + */ +export const dialLogin = async (page: Page): Promise => { + const dialNumber = process.env.DIAL_NUMBER; + if (!dialNumber) { + throw new Error('DIAL_NUMBER is not defined in the .env file'); + } + + console.log('Performing Dial Login...'); + await page.getByTestId('login-option-select').locator('#select-base-triggerid svg').click(); + await page.getByTestId('login-option-Dial Number').click(); +await page.getByTestId('dial-number-input').locator('div').nth(1).click(); + await page.getByRole('textbox', { name: 'Dial Number' }).fill(dialNumber); + await page.getByTestId('teams-dropdown-select').locator('#select-base-triggerid div').click(); + await page.waitForTimeout(200); + await page.locator('[data-testid^="teams-dropdown-"]').nth(1).locator('span, div').first().click(); + await page.getByTestId('login-button').click(); +}; + +export const stationLogout = async (page: Page): Promise => { + console.log('Logging out...'); + await page.getByRole('button', { name: 'Station Logout' }).click(); + await page.waitForTimeout(2000); // Wait for logout to complete + console.log('Logged out successfully.'); +} From e95adf7ce846297581eabb1e8c8308eacc1eb948 Mon Sep 17 00:00:00 2001 From: PrayagGP Date: Tue, 17 Jun 2025 14:43:15 +0530 Subject: [PATCH 002/104] fix(cc-widgets): add timeout to config --- playwright.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playwright.config.ts b/playwright.config.ts index fb196a2de..212d9ef01 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -10,7 +10,7 @@ dotenv.config({path: path.resolve(__dirname, '.env')}); */ export default defineConfig({ testDir: './playwright', - + timeout: 180000, /* Run your local dev server before starting the tests */ webServer: { command: 'yarn workspace samples-cc-react-app serve', @@ -33,7 +33,7 @@ export default defineConfig({ baseURL: 'http://localhost:3000', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'retain-on-failure', + trace: 'on', }, /* Configure projects for major browsers */ From a6494e2f3cf21ed37a374fb41fa848f8568eb3cf Mon Sep 17 00:00:00 2001 From: PrayagGP Date: Tue, 17 Jun 2025 14:44:55 +0530 Subject: [PATCH 003/104] fix(cc-widgets): change login method --- playwright/UserStateTest.spec.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/playwright/UserStateTest.spec.ts b/playwright/UserStateTest.spec.ts index ad91ab2ab..ce5f40b4a 100644 --- a/playwright/UserStateTest.spec.ts +++ b/playwright/UserStateTest.spec.ts @@ -37,10 +37,10 @@ test.describe('๐Ÿ” User State Widget Functionality Suite', () => { const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); if (loginButtonExists) { - await desktopLogin(page); + await extensionLogin(page); } else { await stationLogout(page); - await desktopLogin(page); + await extensionLogin(page); } await expect(page.getByTestId('state-select')).toBeVisible(); @@ -186,8 +186,8 @@ test.describe('๐Ÿ“ฑ User State retention and timer check on page refresh', () => console.log('\n=== ๐Ÿ“ฑ STATION LOGIN & CALLBACK TEST STARTED ==='); // Initial login - /* await oauthLogin(page); - await initialisePage(page);*/ + await oauthLogin(page); + await initialisePage(page); const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); if (loginButtonExists) { @@ -361,8 +361,8 @@ test.describe('โฐ Idle Code State Transitions & Dual Timer Suite', () => { console.log('\n=== โฐ IDLE CODE STATE TRANSITIONS & DUAL TIMER TEST STARTED ==='); // Login and ensure SDK init - /* await oauthLogin(page); - await initialisePage(page);*/ + await oauthLogin(page); + await initialisePage(page); const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); if (loginButtonExists) { @@ -441,9 +441,9 @@ test.describe('๐Ÿ”„ Cross-Page State & Timer Synchronization Suite', () => { // ===== PAGE 1 SETUP ===== console.log('๐Ÿ“ฑ Setting up Page 1...'); - /* await oauthLogin(page); + await oauthLogin(page); await multiLoginEnable(page); - await initialisePage(page);*/ + await initialisePage(page); const loginButtonExists = await page.getByTestId('login-button').isVisible().catch(() => false); if (loginButtonExists) { From 7d675c9b7427717f38639bfd13f037342af79e6b Mon Sep 17 00:00:00 2001 From: PrayagGP Date: Tue, 17 Jun 2025 14:46:16 +0530 Subject: [PATCH 004/104] fix(cc-widgets): fix element locators --- playwright/Utils/initUtils.ts | 8 ++++---- playwright/Utils/stateUtils.ts | 1 - playwright/Utils/stationLoginUtils.ts | 16 ++++++++-------- .../cc/samples-cc-react-app/src/App.tsx | 4 +++- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/playwright/Utils/initUtils.ts b/playwright/Utils/initUtils.ts index 66044049a..5b60ed67b 100644 --- a/playwright/Utils/initUtils.ts +++ b/playwright/Utils/initUtils.ts @@ -12,7 +12,7 @@ export const oauthLogin = async (page: Page): Promise => { await page.goto('http://localhost:3000'); await page.locator('#select-base-triggerid').getByText('Access Token').click(); await page.getByTestId('samples:login_option_oauth').getByText('Login with Webex').click(); - await page.getByRole('button', { name: 'Login with Webex' }).click(); + await page.getByTestId('login with webex button').click(); await page.getByRole('textbox', { name: 'name@example.com' }).fill(process.env.PLAYWRIGHT_USERNAME); await page.getByRole('link', { name: 'Sign in' }).click(); await page.waitForTimeout(3000); @@ -36,9 +36,9 @@ export const multiLoginEnable = async (page: Page): Promise => { export const initialisePage = async (page: Page): Promise => { console.log('Initialising widgets...'); - await page.getByRole('button', { name: 'Init Widgets' }).click(); - await page.getByRole('button', { name: 'Init Widgets' }).click(); + await page.getByTestId('init-widgets-button').click(); + await page.getByTestId('init-widgets-button').click(); - await page.getByTestId('station-login-widget').waitFor({ state: 'visible' }); + await page.getByTestId('station-login-widget').waitFor({ state: 'visible' }); console.log('Widgets initialised successfully!'); }; \ No newline at end of file diff --git a/playwright/Utils/stateUtils.ts b/playwright/Utils/stateUtils.ts index f7a259800..eecb0ac9f 100644 --- a/playwright/Utils/stateUtils.ts +++ b/playwright/Utils/stateUtils.ts @@ -25,7 +25,6 @@ export const changestate = async (page: Page, userState: string): Promise // Change to the desired state await stateItem.click(); - await expect(page.getByTestId('state-select').getByTestId('state-name')).toContainText(userState); console.log(`State changed to "${userState}".`); }; diff --git a/playwright/Utils/stationLoginUtils.ts b/playwright/Utils/stationLoginUtils.ts index bfd5e3742..2ba1159c2 100644 --- a/playwright/Utils/stationLoginUtils.ts +++ b/playwright/Utils/stationLoginUtils.ts @@ -11,9 +11,9 @@ export const desktopLogin = async (page: Page): Promise => { console.log('Performing Desktop Login...'); await page.getByTestId('login-option-select').locator('#select-base-triggerid svg').click(); await page.getByTestId('login-option-Desktop').click(); - await page.getByTestId('teams-dropdown-select').locator('#select-base-triggerid div').click(); + await page.getByTestId('teams-select-dropdown').locator('#select-base-triggerid div').click(); await page.waitForTimeout(200); - await page.locator('[data-testid^="teams-dropdown-"]').nth(1).locator('span, div').first().click(); + await page.locator('[data-testid^="teams-dropdown-"]').nth(0).locator('span, div').first().click(); await page.waitForTimeout(200); await page.getByTestId('login-button').click(); @@ -32,10 +32,10 @@ export const extensionLogin = async (page: Page): Promise => { console.log('Performing Extension Login...'); await page.getByTestId('login-option-select').locator('#select-base-triggerid svg').click(); await page.getByTestId('login-option-Extension').click(); - await page.getByRole('textbox', { name: 'Extension' }).fill(extensionNumber); - await page.getByTestId('teams-dropdown-select').locator('#select-base-triggerid div').click(); + await page.getByTestId('dial-number-input').locator('input').fill(extensionNumber); + await page.getByTestId('teams-select-dropdown').locator('#select-base-triggerid div').click(); await page.waitForTimeout(200); - await page.locator('[data-testid^="teams-dropdown-"]').nth(1).locator('span, div').first().click(); + await page.locator('[data-testid^="teams-dropdown-"]').nth(0).locator('span, div').first().click(); await page.getByTestId('login-button').click(); }; @@ -54,15 +54,15 @@ export const dialLogin = async (page: Page): Promise => { await page.getByTestId('login-option-Dial Number').click(); await page.getByTestId('dial-number-input').locator('div').nth(1).click(); await page.getByRole('textbox', { name: 'Dial Number' }).fill(dialNumber); - await page.getByTestId('teams-dropdown-select').locator('#select-base-triggerid div').click(); + await page.getByTestId('teams-select-dropdown').locator('#select-base-triggerid div').click(); await page.waitForTimeout(200); - await page.locator('[data-testid^="teams-dropdown-"]').nth(1).locator('span, div').first().click(); + await page.locator('[data-testid^="teams-dropdown-"]').nth(0).locator('span, div').first().click(); await page.getByTestId('login-button').click(); }; export const stationLogout = async (page: Page): Promise => { console.log('Logging out...'); - await page.getByRole('button', { name: 'Station Logout' }).click(); + await page.getByTestId('station-logout-button').click(); await page.waitForTimeout(2000); // Wait for logout to complete console.log('Logged out successfully.'); } 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 e0ae67db5..be833e7c7 100644 --- a/widgets-samples/cc/samples-cc-react-app/src/App.tsx +++ b/widgets-samples/cc/samples-cc-react-app/src/App.tsx @@ -414,6 +414,7 @@ const handleSaveEnd = (isComplete: boolean) => { @@ -496,7 +497,7 @@ const handleSaveEnd = (isComplete: boolean) => { }} /> {store.isAgentLoggedIn && ( - )} @@ -547,6 +548,7 @@ const handleSaveEnd = (isComplete: boolean) => { setIsSdkReady(true); }); }} + data-testid="init-widgets-button" > Init Widgets From 63c1ed76c0ae7408bf5fc558e47e667d04cb60b8 Mon Sep 17 00:00:00 2001 From: PrayagGP Date: Tue, 17 Jun 2025 15:17:32 +0530 Subject: [PATCH 005/104] fix(cc-widgets): added data-testid for some checkoxes and updated utils with them --- playwright/Utils/initUtils.ts | 14 +++++++------- playwright/Utils/stationLoginUtils.ts | 2 +- .../cc/samples-cc-react-app/src/App.tsx | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/playwright/Utils/initUtils.ts b/playwright/Utils/initUtils.ts index 5b60ed67b..f02f58a80 100644 --- a/playwright/Utils/initUtils.ts +++ b/playwright/Utils/initUtils.ts @@ -17,21 +17,21 @@ export const oauthLogin = async (page: Page): Promise => { await page.getByRole('link', { name: 'Sign in' }).click(); await page.waitForTimeout(3000); // Check if Init Widgets button is visible after username sign in (Multi session) - const initWidgetsButton = page.getByRole('button', { name: 'Init Widgets' }); + const initWidgetsButton = page.getByTestId('init-widgets-button'); const isInitWidgetsVisible = await initWidgetsButton.isVisible().catch(() => false); if (!isInitWidgetsVisible) { // If Init Widgets button is not visible, proceed with password entry - await page.getByRole('textbox', { name: 'Password' }).fill(process.env.PLAYWRIGHT_PASSWORD); - await page.getByRole('button', { name: 'Sign in' }).click(); + await page.getByRole('textbox', { name: 'Password' }).fill(process.env.PLAYWRIGHT_PASSWORD); + await page.getByRole('button', { name: 'Sign in' }).click(); } - - await page.getByRole('checkbox', { name: 'Show Agent Profile' }).check(); - + + await page.getByTestId('show-agent-profile-checkbox').click(); + } export const multiLoginEnable = async (page: Page): Promise => { - await page.getByText('Enable Multi Login').click(); + await page.getByTestId('multi-login-enable-checkbox').click(); } export const initialisePage = async (page: Page): Promise => { diff --git a/playwright/Utils/stationLoginUtils.ts b/playwright/Utils/stationLoginUtils.ts index 2ba1159c2..e6f7180e6 100644 --- a/playwright/Utils/stationLoginUtils.ts +++ b/playwright/Utils/stationLoginUtils.ts @@ -53,7 +53,7 @@ export const dialLogin = async (page: Page): Promise => { await page.getByTestId('login-option-select').locator('#select-base-triggerid svg').click(); await page.getByTestId('login-option-Dial Number').click(); await page.getByTestId('dial-number-input').locator('div').nth(1).click(); - await page.getByRole('textbox', { name: 'Dial Number' }).fill(dialNumber); + await page.getByTestId('dial-number-input').locator('input').fill(dialNumber); await page.getByTestId('teams-select-dropdown').locator('#select-base-triggerid div').click(); await page.waitForTimeout(200); await page.locator('[data-testid^="teams-dropdown-"]').nth(0).locator('span, div').first().click(); 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 be833e7c7..65fb5c7ee 100644 --- a/widgets-samples/cc/samples-cc-react-app/src/App.tsx +++ b/widgets-samples/cc/samples-cc-react-app/src/App.tsx @@ -487,6 +487,7 @@ const handleSaveEnd = (isComplete: boolean) => { }} /> {  SDK Toggles