Skip to content

test(calling): add transfer consult flows#4939

Open
eigengravy wants to merge 4 commits into
webex:nextfrom
eigengravy:calling-sdk-e2e-tests-call-flows
Open

test(calling): add transfer consult flows#4939
eigengravy wants to merge 4 commits into
webex:nextfrom
eigengravy:calling-sdk-e2e-tests-call-flows

Conversation

@eigengravy
Copy link
Copy Markdown
Member

@eigengravy eigengravy commented Apr 30, 2026

COMPLETES https://jira-eng-sjc12.cisco.com/jira/browse/CAI-7882

This pull request addresses

Follow-up to #4879 — adds 3-user transfer & consult call-flow Playwright E2E
coverage for the Calling SDK on top of the existing 2-user suite, and hardens
call cleanup / selectors that surfaced while iterating on the new flows.

by making the following changes

  • Enables the SET_3USER - PROD and SET_3USER - INT Playwright projects (depend on SET_2USER)
  • Adds suites/set-3user.spec.ts as the 3-user suite entry point
  • Adds test-groups/transfer-tests.ts with blind transfer and consult transfer flows

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Tooling change
  • Internal code refactor

The following scenarios were tested

  • Automated: new 3-user transfer suite (blind + consult transfer) run locally against the Calling SDK samples page via SET_3USER - PROD / SET_3USER - INT
  • Automated: existing 2-user call flows, SDK init, and device registration suites re-run to confirm no regressions
image

The GAI Coding Policy And Copyright Annotation Best Practices

  • GAI was not used (or, no additional notation is required)
  • Code was generated entirely by GAI
  • GAI was used to create a draft that was subsequently customized or modified
  • Coder created a draft manually that was non-substantively modified by GAI
  • Tool used for AI assistance (GitHub Copilot / Other - specify)
    • Github Copilot
    • Other - Claude Code
  • This PR is related to
    • Feature
    • Defect fix
    • Tech Debt
    • Automation

I certified that

  • I have read and followed contributing guidelines
  • I discussed changes with code owners prior to submitting this pull request
  • I have not skipped any automated checks
  • All existing and new tests passed
  • I have updated the documentation accordingly

- Target visible incoming #answer via #incomingsection scope to avoid
  the hidden duplicate in the sample app
- Make cleanupActiveCalls retry-capable with a direct call.end()
  fallback, and invoke it before context teardown
- Fix withTimeout to clear its timer on settle so promises do not leak
  handles
- Tolerate resumeCall being entered from either Hold or Resume state
- Guard unregisterLine against closed pages / already-unregistered
  state, and wait for "Unregistered" rather than "Un registering"
@aws-amplify-us-east-2
Copy link
Copy Markdown

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-4939.d3m3l2kee0btzx.amplifyapp.com

@eigengravy eigengravy marked this pull request as ready for review May 5, 2026 04:55
@eigengravy eigengravy requested a review from a team as a code owner May 5, 2026 04:55
@eigengravy eigengravy changed the title test(calling): add transfer consult flows [WIP] test(calling): add transfer consult flows May 5, 2026
@eigengravy eigengravy added the validated If the pull request is validated for automation. label May 5, 2026
Enable the SET_3USER Playwright projects (PROD + INT) and add the
transfer/consult test group that runs against them. Picks up the
checkpoint that was lost during an earlier reset + rebase.

- packages/calling/playwright.config.ts: enable SET_3USER projects
- packages/calling/playwright/suites/set-3user.spec.ts: suite entry
- packages/calling/playwright/test-groups/transfer-tests.ts: blind and
  consult transfer flows
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c019e875a1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

// Page may have closed during teardown
}
});
test.afterAll(() => group.teardown());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Use a defined cleanup method in afterAll

setupThreeUserGroup() returns setup, cleanupAfterEach, and getters, but no teardown method, so test.afterAll(() => group.teardown()) throws TypeError: group.teardown is not a function when this describe finishes. That causes this suite to fail during teardown and skips the intended final cleanup path for the shared 3-user contexts.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea0d907a9d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +125 to +128
name: 'SET_3USER - PROD',
dependencies: ['SET_2USER - PROD'],
testDir: './playwright/suites',
testMatch: USER_SETS.SET_3USER.testSuite,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Gate SET_3USER projects behind USER_6 OAuth prerequisites

Enabling SET_3USER here makes USER_6 mandatory for every default Playwright run, but OAuth setup still treats USER_6 as optional (REQUIRED_OAUTH_ROLES excludes it), so environments without USER_6_* credentials/token will pass OAuth setup and then fail later when TestManager requests getToken('USER_6') for the third context. This turns the new projects into a deterministic runtime failure in CI/local setups that only provision USER_1..USER_5.

Useful? React with 👍 / 👎.

await settleUi(callerPage);
await callerPage.locator(CALLING_SELECTORS.TRANSFER_BTN).click({timeout: AWAIT_TIMEOUT});
await callerPage.waitForTimeout(POST_ACTION_SETTLE_MS);
} finally {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe Add a Catch here, in case something actually fails.
Rest Looks Good.

} from '../utils/call';
import {CALLING_SELECTORS, AWAIT_TIMEOUT} from '../constants';

const UI_SETTLE_MS = 3000;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We have timeouts.ts file right where we defined timeout related constants , move these there


export function transferTests() {
test.describe('Transfer - Blind', () => {
test.describe.configure({mode: 'serial', timeout: 240000});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We can constant added for this timeout that we are using in describe blocks and mode as well

await group.teardown();
});

test('CALL-024: Consult transfer - caller consults then commits transfer to third party', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Add scenarios where user on hold drops the call before consult transfer completes and scenarios where transferror puts the consulted user on hold and resumes the initial user.

);
};

const setupThreeUserGroup = () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These methods can be present in util file right. If we have separate folder created to keep utility methods, then these may also qualify to be moved there

await group.teardown();
});

test('CALL-009: Blind transfer completion - caller transfers call to third party', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How are we coming up with these indexes: CALL-009 and then CALL-026 even though both are for blind transfer.

await callerPage.locator(CALLING_SELECTORS.TRANSFER_BTN).click({timeout: AWAIT_TIMEOUT});
await callerPage.waitForTimeout(POST_ACTION_SETTLE_MS);

await waitForCallDisconnect(callerPage, 30000);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same comment here

await waitForCallDisconnect(calleePage);
});

test('CALL-027: Blind transfer rejection - transfer target rejects, original call remains', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In case of blind transfer, transferror would be disconnected from the call irrespective if the transfer is accepted or not I believe

await Promise.all([waitForCallDisconnect(callerPage), waitForCallDisconnect(calleePage)]);
});

test('CALL-017: ALL_CALLS_CLEARED - fires after last call ends (consult transfer)', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What is the significance of this test ?

await Promise.all([waitForCallDisconnect(callerPage), waitForCallDisconnect(calleePage)]);
});

test('CALL-004: Remote busy handling - caller dials callee already on a call', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please elaborate this test, I don't fully understand. We establish call with transferpage and calleePage and then caller Page is busy.

// use: {...browserOptions[PW_BROWSER], testEnv: 'int'} as any,
// },
{
name: 'SET_3USER - PROD',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As discussed, please come with a naming convention followed across all client modules

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

validated If the pull request is validated for automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants