Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion playwright/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ OAuth setup behavior (`playwright/global.setup.ts`):
- expands `USER_SETS` into set-scoped env keys
- builds OAuth set groups dynamically from `USER_SETS` (chunk size `2`)
- runs one parallel OAuth worker per generated group
- each group uses `OAUTH_BATCH_SIZE=4` internally
- each group uses `OAUTH_BATCH_SIZE=2` internally (with 3s delay between batches)
- with current 9 sets this resolves to 5 groups: `[SET_1,SET_2]`, `[SET_3,SET_4]`, `[SET_5,SET_6]`, `[SET_7,SET_8]`, `[SET_9]`
- optionally collects dial-number token
- writes all token/env updates in one final upsert pass
Expand Down
11 changes: 6 additions & 5 deletions playwright/Utils/initUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Page, expect, BrowserContext} from '@playwright/test';
import dotenv from 'dotenv';
import {BASE_URL, AWAIT_TIMEOUT, WIDGET_INIT_TIMEOUT, UI_SETTLE_TIMEOUT} from '../constants';
import {BASE_URL, AWAIT_TIMEOUT, NETWORK_OPERATION_TIMEOUT, WIDGET_INIT_TIMEOUT, UI_SETTLE_TIMEOUT} from '../constants';

dotenv.config();

Expand Down Expand Up @@ -67,10 +67,11 @@ export const oauthLogin = async (page: Page, username: string, customPassword?:
await page.locator('#select-base-triggerid').getByText('Access Token').click({timeout: AWAIT_TIMEOUT});
await page.getByTestId('samples:login_option_oauth').getByText('Login with Webex').click({timeout: AWAIT_TIMEOUT});
await page.getByTestId('samples:login_with_webex_button').click({timeout: AWAIT_TIMEOUT});
await page.getByRole('textbox', {name: 'name@example.com'}).fill(username, {timeout: AWAIT_TIMEOUT});
await page.getByRole('link', {name: 'Sign in'}).click({timeout: AWAIT_TIMEOUT});
await page.getByRole('textbox', {name: 'Password'}).fill(password, {timeout: AWAIT_TIMEOUT});
await page.getByRole('button', {name: 'Sign in'}).click({timeout: AWAIT_TIMEOUT});
await page.waitForURL(/idbroker.*\.webex\.com/, {timeout: NETWORK_OPERATION_TIMEOUT});
await page.getByRole('textbox', {name: 'name@example.com'}).fill(username, {timeout: NETWORK_OPERATION_TIMEOUT});
await page.getByRole('link', {name: 'Sign in'}).click({timeout: NETWORK_OPERATION_TIMEOUT});
await page.getByRole('textbox', {name: 'Password'}).fill(password, {timeout: NETWORK_OPERATION_TIMEOUT});
await page.getByRole('button', {name: 'Sign in'}).click({timeout: NETWORK_OPERATION_TIMEOUT});
};

/**
Expand Down
2 changes: 1 addition & 1 deletion playwright/ai-docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ yarn test:e2e --project=SET_1
- `[SET_5, SET_6]`
- `[SET_7, SET_8]`
- `[SET_9]`
- Each group uses batch size 4 internally (`OAUTH_BATCH_SIZE=4`).
- Each group uses batch size 2 internally (`OAUTH_BATCH_SIZE=2`) with a 3s delay between batches.
- Dial-number token is collected when configured.
- All env/token updates are written once via single `.env` upsert.

Expand Down
7 changes: 6 additions & 1 deletion playwright/global.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ const fs = require('fs');
const path = require('path');

const ENV_PATH = path.resolve(__dirname, '../.env');
const OAUTH_BATCH_SIZE = 4;
const OAUTH_BATCH_SIZE = 2;
const OAUTH_SET_GROUP_SIZE = 2;
const OAUTH_BATCH_DELAY_MS = 3000;

type EnvUpdateMap = Record<string, string>;

Expand Down Expand Up @@ -117,6 +118,10 @@ const collectTokensInBatches = async (browser: Browser, tasks: OAuthTask[]): Pro
batch.forEach((task, batchIndex) => {
tokenUpdates[task.envKey] = batchTokens[batchIndex];
});

if (index + OAUTH_BATCH_SIZE < tasks.length) {
await new Promise((resolve) => setTimeout(resolve, OAUTH_BATCH_DELAY_MS));
}
}

return tokenUpdates;
Expand Down
16 changes: 11 additions & 5 deletions playwright/test-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -638,13 +638,19 @@ export class TestManager {
}

const envTokens = this.getEnvTokens();
await this.setupPageWithWidgets(this.multiSessionAgent1Page, envTokens.agent1AccessToken);
}

// Setup multi-session page with widgets - only called when needed for multi-session tests
await loginViaAccessToken(this.multiSessionAgent1Page, envTokens.agent1AccessToken);

await Promise.all([enableMultiLogin(this.multiSessionAgent1Page), enableAllWidgets(this.multiSessionAgent1Page)]);
async ensureMultiSessionPage(browser: Browser): Promise<void> {
if (this.multiSessionAgent1Page) {
return;
}

await initialiseWidgets(this.multiSessionAgent1Page);
const envTokens = this.getEnvTokens();
this.multiSessionContext = await browser.newContext({ignoreHTTPSErrors: true});
this.multiSessionAgent1Page = await this.multiSessionContext.newPage();
await enableMultiLogin(this.agent1Page);
await this.setupPageWithWidgets(this.multiSessionAgent1Page, envTokens.agent1AccessToken);
}

// Specific setup methods that use the universal setup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import {test, expect} from '@playwright/test';
import {
cancelConsult,
consultOrTransfer,
clearAdvancedCapturedLogs,
waitForPrimaryCallAfterConsult,
verifyConsultStartSuccessLogs,
} from '../Utils/advancedTaskControlUtils';
import {changeUserState, verifyCurrentState} from '../Utils/userStateUtils';
import {createCallTask, acceptIncomingTask} from '../Utils/incomingTaskUtils';
Expand Down Expand Up @@ -243,12 +241,11 @@ export default function createAdvanceCombinationsTests() {
await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE);
await createCallTask(testManager.callerPage!, process.env[`${testManager.projectName}_ENTRY_POINT`]!);
await acceptIncomingTask(testManager.agent1Page, TASK_TYPES.CALL);
clearAdvancedCapturedLogs();
await consultOrTransfer(testManager.agent1Page, 'entryPoint', 'consult', process.env.PW_ENTRYPOINT_NAME!);
await expect(testManager.agent1Page.getByTestId('cancel-consult-btn')).toBeVisible();
await verifyConsultStartSuccessLogs();
await cancelConsult(testManager.agent1Page);
await testManager.agent1Page.waitForTimeout(1000);
await verifyCurrentState(testManager.agent1Page, USER_STATES.ENGAGED);
});

test.afterAll(async () => {
Expand Down
5 changes: 4 additions & 1 deletion playwright/tests/basic-task-controls-test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from '../Utils/taskControlUtils';
import {submitWrapup} from '../Utils/wrapupUtils';
import {USER_STATES, TASK_TYPES, WRAPUP_REASONS, ACCEPT_TASK_TIMEOUT} from '../constants';
import {handleStrayTasks, waitForState} from '../Utils/helperUtils';
import {TestManager} from '../test-manager';

// Extract test functions for cleaner syntax
Expand All @@ -34,6 +35,7 @@ export default function createCallTaskControlsTests() {
const projectName = testInfo.project.name;
testManager = new TestManager(projectName);
await testManager.setupForIncomingTaskDesktop(browser);
await handleStrayTasks(testManager.agent1Page);
});

afterAll(async () => {
Expand All @@ -55,8 +57,9 @@ export default function createCallTaskControlsTests() {

test('Call task - create call and verify all control buttons are visible', async () => {
// Ensure routable state before creating call task.
await handleStrayTasks(testManager.agent1Page);
await changeUserState(testManager.agent1Page, USER_STATES.AVAILABLE);
await verifyCurrentState(testManager.agent1Page, USER_STATES.AVAILABLE);
await waitForState(testManager.agent1Page, USER_STATES.AVAILABLE);

// Create call task
await createCallTask(testManager.callerPage!, process.env[`${testManager.projectName}_ENTRY_POINT`]!);
Expand Down
16 changes: 7 additions & 9 deletions playwright/tests/user-state-test.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {test, expect} from '@playwright/test';
import {agentRelogin} from '../Utils/initUtils';
import {agentRelogin, enableMultiLogin} from '../Utils/initUtils';
import {stationLogout, telephonyLogin} from '../Utils/stationLoginUtils';
import {
getCurrentState,
Expand All @@ -19,6 +19,7 @@ export default function createUserStateTests() {
const projectName = testInfo.project.name;
testManager = new TestManager(projectName);
await testManager.basicSetup(browser);
await enableMultiLogin(testManager.agent1Page);
Comment on lines 21 to +22

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enable multi-login before widget initialization

Here basicSetup() has already initialized the widgets via the desktop pageSetup() path, which disables multi-login before SDK initialization; the sample app warns that changing samples:multi-login-enable-checkbox after SDK initialization has no effect (widgets-samples/cc/samples-cc-react-app/src/App.tsx lines 636-638). In filtered runs of just should test multi-session synchronization (or if the earlier reload test is skipped), agent1's SDK remains single-login while the second session is initialized, so the test depends on a previous reload to pass instead of being independently runnable. Enable multi-login before widget initialization or reload/reinitialize immediately after setting it.

Useful? React with 👍 / 👎.

// Handle the station login manually like in the original
const loginButtonExists = await testManager.agent1Page
.getByTestId('login-button')
Expand Down Expand Up @@ -115,16 +116,13 @@ export default function createUserStateTests() {
});

test('should test multi-session synchronization', async () => {
// Create multi-session page since basicSetup doesn't include it
if (!testManager.multiSessionAgent1Page) {
if (!testManager.multiSessionContext) {
testManager.multiSessionContext = await testManager.agent1Context.browser()!.newContext();
}
testManager.multiSessionAgent1Page = await testManager.multiSessionContext.newPage();
const browser = testManager.agent1Page.context().browser();
if (!browser) {
throw new Error('Browser not available for multi-session setup');
}

await testManager.setupMultiSessionPage();
await testManager.ensureMultiSessionPage(browser);
const multiSessionPage = testManager.multiSessionAgent1Page!;
await expect(multiSessionPage.getByTestId('state-select')).toBeVisible();

await changeUserState(testManager.agent1Page, USER_STATES.MEETING);
await verifyCurrentState(testManager.agent1Page, USER_STATES.MEETING);
Expand Down
Loading