test(calling): added palywright E2E test for Calling sdk contacts#4960
test(calling): added palywright E2E test for Calling sdk contacts#4960vivekv1504 wants to merge 9 commits into
Conversation
There was a problem hiding this comment.
💡 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".
| contacts | ||
| .filter((c) => c.contactType === 'CUSTOM') | ||
| .map((c) => safeDeleteContact(page, c.contactId)) | ||
| ); |
There was a problem hiding this comment.
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 👍 / 👎.
|
This pull request is automatically being deployed by Amplify Hosting (learn more). |
There was a problem hiding this comment.
💡 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) => [ |
There was a problem hiding this comment.
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'}); |
There was a problem hiding this comment.
removed the ui in variable name
| await expectGroupInTable(tm.page, displayName); | ||
|
|
||
| // Delete via the table row button | ||
| await uiDeleteGroupByName(tm.page, displayName); |
There was a problem hiding this comment.
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 [ |
There was a problem hiding this comment.
window as any).contacts remove everywhere
There was a problem hiding this comment.
done !, remove window as any wherever is presented
There was a problem hiding this comment.
💡 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); |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
💡 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}); |
There was a problem hiding this comment.
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 👍 / 👎.
| await expect(tm.page.locator('#contactsTableId')).toBeAttached(); | ||
| await expect(tm.page.locator('#contactGroupsTableId')).toBeAttached(); |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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);
There was a problem hiding this comment.
💡 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".
| await deleteFirst('#contactsTableId tr button.btn--red', 50); | ||
| await deleteFirst('#contactGroupsTableId tr button.btn--red', 50); |
There was a problem hiding this comment.
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 👍 / 👎.

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 suitesAdded
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
displayNameis visible in the tableSuite 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
displayNamedoes 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 nowindowSDK access)Updated
packages/calling/playwright/playwright.config.ts— addedSET_CONTACTS - PRODandSET_CONTACTS - INTprojectsUpdated
packages/calling/playwright/test-data.ts— registeredSET_CONTACTSuser set usingUSER_6accountChange Type
The following scenarios were tested
< ENUMERATE TESTS PERFORMED, WHETHER MANUAL OR AUTOMATED >
The GAI Coding Policy And Copyright Annotation Best Practices
I certified that
Make sure to have followed the contributing guidelines before submitting.