Skip to content

test(calling): add Playwright E2E tests for 2-user call flows#4879

Open
eigengravy wants to merge 2 commits intowebex:nextfrom
eigengravy:calling-sdk-e2e-tests-call-flows
Open

test(calling): add Playwright E2E tests for 2-user call flows#4879
eigengravy wants to merge 2 commits intowebex:nextfrom
eigengravy:calling-sdk-e2e-tests-call-flows

Conversation

@eigengravy
Copy link
Copy Markdown
Member

@eigengravy eigengravy commented Apr 20, 2026

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

This pull request addresses

Playwright E2E tests for the Calling SDK covering 2-user call flows — lifecycle, media, controls, hold, keepalive, errors, and edge cases. Builds on the SDK init + registration suite (#4799) and plugs into the same phased-parallel architecture via a new SET_2USER worker.

Architecture

Extends the existing phased parallel execution model with a new project, SET_2USER, that runs 2-party call tests in parallel with the single-user registration sets (SET_1, SET_2, SET_3).

PROD vs INT account mapping

  • PRODSET_2USER uses dedicated USER_4 + USER_5 accounts, so it runs fully in parallel with SET_1 / SET_2 / SET_3 (which use USER_1/USER_2/USER_3).
  • INTUSER_4 / USER_5 are aliases for the same 3 INT accounts used by the registration sets, so SET_2USER - INT declares dependencies: ['SET_1 - INT', 'SET_2 - INT', 'SET_3 - INT'] to serialize after them and honor the one-account-per-browser constraint.

A SET_3USER project scaffold (for transfer flows) is stubbed in the config but commented out — it will be enabled in a follow-up PR (CAI-7741 / transfer tests).

Changes

New test infrastructure

  • playwright/utils/call.ts — Call action helpers: makeCall, answerCall, endCall, rejectCall, holdCall, resumeCall, sendDTMF, establishCall, getMediaStreams, cleanupActiveCalls, plus disconnect/established waiters with Node-side timeout racing (Playwright waitForFunction timeouts can be swallowed by WebRTC work on the page)
  • playwright/test-manager.ts — Extended with a media: true setup option (clicks "Get Media Streams" after registration) and now deregisters/closes stale contexts before reuse and on cleanup() to prevent leaked backend registrations across serial test groups
  • playwright/test-data.ts — Added USER_4USER_6 roles, getPhoneNumber() helper, and enabled SET_2USER / SET_3USER entries in USER_SETS
  • playwright.config.ts — New SET_2USER - PROD / SET_2USER - INT projects with env-specific dependencies; SET_3USER stubs left commented

Test groups (shared logic, not spec files)

  • call-lifecycle.ts — Basic 2-party lifecycle + media/disconnect group (fresh contexts per group)
  • call-controls.ts — Mute/unmute, DTMF, network flap, and hold/resume (including hold+disconnect races and hold/resume API failures via route interception)
  • call-errors.ts — Connect timeout, ROAP failure, invalid destination, plus edge cases (resume/disconnect race, deregister mid-call, calling unregistered endpoint)
  • call-keepalive.ts — Call-level keepalive (postStatus) success, 401 teardown, 500 transient recovery, and max-retry exhaustion

Suite entry point

  • suites/set-2user.spec.ts — Thin spec file wiring all test groups for the SET_2USER worker

OAuth / fixtures (follow-up fix commit)

  • test-data.ts — New REQUIRED_OAUTH_ROLES list (USER_1USER_5) that the OAuth setup validates against
  • utils/oauth.setup.ts — Fail fast when any required role is missing _EMAIL / _PASSWORD, instead of only failing on the first account. USER_6 remains optional until SET_3USER ships.

Techniques used

  • Playwright route interception to inject failures (401 on keepalive, 500 on hold/resume, timeout on call setup, ROAP error responses)
  • page.evaluate() to inspect callingClient.getActiveCalls() state (connected, muted, hold, call counts)
  • context.setOffline(true/false) to simulate brief network disruption mid-call (CALL-016)
  • Per-group fresh browser contexts via TestManager.beforeAll; afterEach cleans up any lingering active calls and waits a short settle time before the next test
  • Node-side withTimeout race to guard against Playwright waitForFunction timeouts being blocked by WebRTC

Test Cases

Call Lifecycle (3 tests)

Test Description
CALL-001 Outgoing call happy path — full event sequence, both sides connected
CALL-002 Incoming call answer flow — verify callee perspective
CALL-003 Incoming call reject flow — callee rejects, caller receives rejection

Call Lifecycle — Media & Disconnect (5 tests)

Test Description
CALL-014 Local disconnect — caller hangs up
CALL-015 Remote disconnect — callee hangs up
CALL-033 Unanswered call — caller hangs up after ring timeout
CALL-012 ROAP success — remote audio srcObject populated on both sides
CALL-018 Call metrics — rtp-rxstat / rtp-txstat VQ metrics present during call

Call Controls (3 tests)

Test Description
CALL-030 Mute and unmute — isMuted() toggles correctly
CALL-008 DTMF send — digit sequence delivered during active call
CALL-016 Network flap with active call — call survives brief offline/online toggle

Call Hold (3 tests)

Test Description
CALL-005 Hold and resume — multiple cycles
CALL-019 Hold then remote disconnect — callee hangs up while caller holds
CALL-020 Hold then immediate disconnect — caller holds and immediately hangs up

Call Hold Errors (2 tests)

Test Description
CALL-006 Hold API failure — hold_error event emitted (route-injected 500)
CALL-007 Resume API failure — resume_error event emitted (route-injected 500)

Call Keepalive (4 tests)

Test Description
CALL-024 Keepalive success — postStatus 200 keeps call alive
CALL-025 Keepalive 401 — expired token tears down call
CALL-026 Keepalive 500 with retry — transient failure then recovery
CALL-027 Keepalive max retries exhausted — all status POSTs fail

Call Errors (3 tests)

Test Description
CALL-011 Call connect timeout — no stuck call on setup failure
CALL-013 ROAP error — media negotiation failure triggers teardown
CALL-028 Call to invalid destination — call fails and cleans up

Call Edge Cases (3 tests)

Test Description
CALL-021 Resume and disconnect race — concurrent operations on both endpoints
CALL-034 Deregister during active call — callee deregisters mid-call
CALL-035 Call to unregistered endpoint — call fails and cleans up

Total: 26 call tests across 8 serial groups, all running in the SET_2USER worker.

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 (e.g., refactoring was performed by GAI on manually written code)
  • 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

@eigengravy eigengravy requested a review from a team as a code owner April 20, 2026 03:58
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: 66f8e72c61

ℹ️ 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 +10 to +14
const withTimeout = <T>(promise: Promise<T>, ms: number, label: string): Promise<T> =>
Promise.race([
promise,
new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);
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 Clear timeout handles when racing call setup waits

withTimeout races the target promise against a setTimeout but never cancels the timer when the main promise resolves, so every successful call path leaves a live timeout handle (up to 60s in establishCall) in the worker process. Because this helper is used multiple times per call attempt, the suite can accumulate open timers, adding avoidable tail latency and causing workers to linger after tests complete; store the timeout id and clear it when either branch settles.

Useful? React with 👍 / 👎.

Comment on lines +117 to +120
SET_3USER: {
name: 'SET_3USER',
accounts: ['USER_4', 'USER_5', 'USER_6'],
testSuite: 'set-3user.spec.ts',
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 Remove disabled SET_3USER from active user-set registry

Registering SET_3USER in USER_SETS makes OAuth setup treat USER_6 as an active role via uniqueRoles, even though the SET_3USER Playwright projects are commented out and USER_6 is documented as optional. This breaks the SKIP_AUTH=true fast path (it now requires a USER_6 token to skip), so environments that intentionally omit USER_6 credentials/tokens will unexpectedly run the portal login flow and fail.

Useful? React with 👍 / 👎.

@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-4879.d3m3l2kee0btzx.amplifyapp.com

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant