Skip to content

test(calling): added palywright E2E test for Calling sdk contacts#4960

Open
vivekv1504 wants to merge 9 commits into
webex:nextfrom
vivekv1504:callingSDK-contacts
Open

test(calling): added palywright E2E test for Calling sdk contacts#4960
vivekv1504 wants to merge 9 commits into
webex:nextfrom
vivekv1504:callingSDK-contacts

Conversation

@vivekv1504
Copy link
Copy Markdown
Contributor

@vivekv1504 vivekv1504 commented May 6, 2026

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

This pull request addresses

The Contacts supplementary service in the Calling SDK lacked E2E test coverage.
This change adds a Playwright E2E test suite for the ContactsClient API, covering contact list retrieval, contact group CRUD, contact creation, and contact deletion against both PROD and INT environments. Linked to Jira ticket CAI-7877.

by making the following changes

  • Added packages/calling/playwright/suites/contacts.spec.ts — entry point that registers all 4 Contacts test suites

  • Added packages/calling/playwright/test-groups/contacts.ts — 15 test cases (CONT-001 to CONT-017, with gaps at 012, 014) across 4 suites, all driven through the sample-app UI:

    Suite 1 — Contact List (CONT-001 to CONT-004):
    • Get Contacts renders both the contacts table and the groups table
    • Created CUSTOM contact appears in the contacts table with the correct type
    • Created contact group appears in the groups table
    • CUSTOM contact's decrypted displayName is visible in the table

    Suite 2 — Contact Group CRUD (CONT-005 to CONT-010):
    • Create NORMAL group — appears in the groups table with correct type
    • Create EXTERNAL group — stored with correct type in the table
    • Duplicate displayName does not show success status
    • Created group appears in a subsequent Get Contacts call
    • Delete group — row is removed from the table
    • Deleted group does not reappear in a subsequent Get Contacts call

    Suite 3 — Create Contact (CONT-011, CONT-013, CONT-015, CONT-016):
    • CUSTOM contact via form — shows success status
    • CUSTOM contact is auto-assigned to a default group, visible in the Groups column
    • CLOUD contact with empty contactId — shows error status in the app
    • Newly created CUSTOM contact appears in the Get Contacts table with correct type

    Suite 4 — Delete Contact (CONT-017):
    • Contact is removed from the table after clicking the Delete button
    • Re-fetching confirms the deleted contact no longer appears

  • Added packages/calling/playwright/utils/contacts.ts — UI helper library (form fills, button clicks, table assertions, UI-based cleanup with no window SDK access)

  • Updated packages/calling/playwright/playwright.config.ts — added SET_CONTACTS - PROD and SET_CONTACTS - INT projects

  • Updated packages/calling/playwright/test-data.ts — registered SET_CONTACTS user set using USER_6 account

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

< ENUMERATE TESTS PERFORMED, WHETHER MANUAL OR AUTOMATED >

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 - Please Specify
  • 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

Make sure to have followed the contributing guidelines before submitting.

@vivekv1504 vivekv1504 requested a review from a team as a code owner May 6, 2026 18:41
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: 7a2382f4f1

ℹ️ 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 +169 to +172
contacts
.filter((c) => c.contactType === 'CUSTOM')
.map((c) => safeDeleteContact(page, c.contactId))
);
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 Limit cleanup to records created by these tests

When the contacts suite runs against the configured PROD/INT projects, this cleanup deletes every existing CUSTOM contact for the authenticated account, not just the CONT-* records created by the test. If the E2E account contains seeded/manual contacts, each suite's afterAll will permanently remove them; cleanup should track created IDs or filter by the test prefix before deleting.

Useful? React with 👍 / 👎.

@vivekv1504 vivekv1504 added the validated If the pull request is validated for automation. label May 6, 2026
@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-4960.d3m3l2kee0btzx.amplifyapp.com

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: ffea40f305

ℹ️ 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".


// Single-user registration sets (generated from USER_SETS, depend on OAuth)
...['SET_1', 'SET_2', 'SET_3'].flatMap((key) => [
...['SET_1', 'SET_2', 'SET_3', 'SET_CONTACTS'].flatMap((key) => [
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 Serialize contacts INT with the aliased account

Adding SET_CONTACTS to the generated single-user projects makes SET_CONTACTS - INT depend only on OAuth, so it can run in parallel with SET_3 - INT. This same config documents that INT USER_4/5/6 are aliases for the three registration accounts, while the contacts suite uses USER_6 and registers a line in every beforeAll; when the full INT matrix runs, the aliased account can be active in two browser sessions and hit the registration conflicts that the existing SET_2USER - INT dependency chain avoids. Please serialize the contacts INT project behind the corresponding registration set or give it a truly dedicated INT account.

Useful? React with 👍 / 👎.

test('CONT-004: getContacts - CUSTOM contact shows its decrypted displayName in the table', async () => {
const displayName = `CONT-004 Decrypted ${Date.now()}`;

await uiCreateCustomContact(tm.page, {displayName, phone: '+15005550001'});
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.

remove ui?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed the ui in variable name

await expectGroupInTable(tm.page, displayName);

// Delete via the table row button
await uiDeleteGroupByName(tm.page, displayName);
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.

here as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

removed wherever have ui in variablename and chnage into proper variable name

* Used for cleanup hooks (afterEach / afterAll).
*/
export const deleteContactGroup = (page: Page, groupId: string): Promise<ContactsResponse> =>
page.evaluate(async ([id]) => (window as any).contacts.deleteContactGroup(id), [groupId] as [
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.

window as any).contacts remove everywhere

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done !, remove window as any wherever is presented

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: 5674f027a0

ℹ️ 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".

export const uiDeleteContactByName = async (page: Page, displayName: string): Promise<void> => {
const row = page.locator('#contactsTableId tr').filter({hasText: displayName});
await row.locator('button.btn--red').click();
await page.waitForTimeout(1500);
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 Await the delete operation before refetching contacts

When the backend delete call takes longer than the fixed 1.5s sleep, Playwright's click only waits for the button handler to be invoked, not for the async deleteContact(...) in the sample app to finish. The tests then immediately call clickGetContacts(), which can race ahead of the deletion and re-render the row, making CONT-018 fail intermittently even though the SDK would have deleted the contact within its normal operation timeout. Please wait on the delete network response or poll the SDK/table state instead of using a fixed delay; the same pattern in the group delete helper has the same race.

Useful? React with 👍 / 👎.

@vivekv1504
Copy link
Copy Markdown
Contributor Author

Screenshot 2026-05-18 at 20 55 25

@vivekv1504 vivekv1504 requested a review from rsarika May 18, 2026 19:32
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: e780339cd5

ℹ️ 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".

* enabled in the UI. Replaces the previous window.contacts readiness check.
*/
export const verifyContactsClientReady = async (page: Page): Promise<void> => {
await expect(page.locator('#getContacts')).toBeEnabled({timeout: OPERATION_TIMEOUT});
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 Restore an actual contacts-client readiness gate

verifyContactsClientReady() now only asserts #getContacts is enabled, but in the sample app that button is statically enabled in markup and not gated by contacts initialization. This means beforeAll can pass even when the Contacts client is not actually ready, so regressions in contacts wiring surface later as opaque test failures instead of failing fast at setup. Use a readiness signal tied to client initialization (for example, checking the client object or a successful contacts fetch result) rather than button enabled state.

Useful? React with 👍 / 👎.

Comment on lines +63 to +64
await expect(tm.page.locator('#contactsTableId')).toBeAttached();
await expect(tm.page.locator('#contactGroupsTableId')).toBeAttached();
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 Validate getContacts output instead of static table attachment

This assertion does not verify getContacts behavior because #contactsTableId and #contactGroupsTableId are always present in the page DOM; toBeAttached() will pass even if the click handler fails or the API call errors. As written, CONT-001 can report success without proving any contacts data was fetched/rendered, leaving a coverage gap for the main flow this suite is meant to test.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Before

Confirmed the issue: #contactsTableId and #contactGroupsTableId are empty elements hardcoded in index.html, so toBeAttached() would have passed even with a broken click handler or failed API.

 await clickGetContacts(tm.page);
 await expect(tm.page.locator('#contactsTableId')).toBeAttached();
 await expect(tm.page.locator('#contactGroupsTableId')).toBeAttached();

After

CONT-001 now seeds a contact and a group via the UI, then clicks Get Contacts and asserts both seeded entries appear in the rendered tables. This validates the full click → API → render flow end-to-end and matches the existing CONT-002 / CONT-003 pattern.

 const seedContact = `CONT-001 Seed Contact ${Date.now()}`;
 const seedGroup = `CONT-001 Seed Group ${Date.now()}`;
 await createCustomContact(tm.page, {displayName: seedContact, phone: '+15005550001'});
 await waitForContactSuccess(tm.page);
 await createContactGroup(tm.page, seedGroup);
 await waitForGroupSuccess(tm.page);

 await clickGetContacts(tm.page);
 await expectContactInTable(tm.page, seedContact);
 await expectGroupInTable(tm.page, seedGroup);

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: dcc485f3a5

ℹ️ 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 +242 to +243
await deleteFirst('#contactsTableId tr button.btn--red', 50);
await deleteFirst('#contactGroupsTableId tr button.btn--red', 50);
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 hard cap from teardown contact cleanup

The teardown helper only attempts 50 deletions for contacts and 50 for groups, so shared or long-lived test accounts with more entries will keep leftover data after afterEach/afterAll; that stale state can break later cases (for example duplicate-name assertions) and makes this suite non-idempotent across repeated runs. Cleanup should iterate until no delete buttons remain (or page state confirms empty tables) instead of stopping at a fixed count.

Useful? React with 👍 / 👎.

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