When your page renders multiple instances of the same element or component, your locator strategy needs to reliably be able to target the specific element that our test cares about.
Using indexes might not work if the order or count of the elements is not consistent. Using dynamic test IDs (such as edit-${username}-button) can sometimes be complex to create and might be brittle depending on the formatting of the dynamic part of the locator.
Using what I call ‘cousin’ locators to help fetch the element you want is a much more robust and reliable solution.
Using cousin locators
In the example below, I want to click the ‘Edit’ button for the user Dawn Summers.
If we just try clicking on the edit button like below, the test will fail as there are multiple elements that match that locator.
await page.getByTestId('edit-profile-button').click()
So we need to narrow down our search so it only looks at the button in Dawn’s profile container.
We can start by choosing a unique element inside each container. User ID is a good example in this scenario. Assuming Dawn’s user ID remains constant between test runs, we can get this element using the following:
page.getByTestId(‘user-id’).filter({hasText: ‘920437’})
From this, we can work upwards to get the user container.
getProfileContainer(userId: string) {
const userIdElement = page.getByTestId(‘user-id’).filter({hasText: userId})
return page.getByTestId(‘profile-container’).filter({has: userIdElement})
}
Now that we have the correct profile container, we can look within this container and click on the edit button we need.
async clickEditProfileButton(userId: string) {
const profileContainer = getProfileContainer(userId);
await profileContainer.getByTestId(‘edit-profile-button’).click()
}
And that’s it. We can now reliably click the corresponding button based on the user ID we’ve supplied. Here’s a full look at this code in a page object model file.
export class profileList {
readonly page: Page;
readonly profileContainer: Locator;
readonly userId: Locator;
readonly editProfileButton: Locator;
constructor(page: Page) {
this.page = page;
this.profileContainer = page.getByTestId(‘profile-container’);
this.userId = page.getByTestId(‘user-id’);
this.editProfileButton = page.getByTestId(‘edit-profile-button’);
}
getProfileContainer(userId: string) {
const userIdElement = this.userId.filter({hasText: userId})
return this.profileContainer.filter({has: userIdElement})
}
async clickEditProfileButton(userId: string) {
const profileContainer = this.getProfileContainer(userId);
await profileContainer.locator(this.editProfileButton).click()
}
}

Top comments (0)