I’m trying to automate a test case where I need to click the fifth ‘Delete’ button on a dashboard page. The problem is, there are 10 ‘Delete’ buttons in total, and they all look exactly the same in the HTML.
Here is what I’ve tried so far:
I used the Web Recorder to spy and capture the button. It created a Test Object, but when I run the script, it always just clicks the very first ‘Delete’ button on the page, not the fifth one.
I tried changing the Selection Method to Attributes and checked different boxes, but since the IDs are dynamic (they change every time the page reloads) and the class names are identical, Katalon either clicks the first one or throws an ElementNotVisibleException.
If you can post even the UI image it would clear the picture. Solution
But in the situation which you are describing, I assume the delete button would be in table in each specific rows. If this is the case then create locator with placeholder, and the placeholder will be targeting to some specific unique item in the table row. This will resolve the issue and hit the dedicated button delete.
But to get more precise answer, kindly clarify the issue
To click the fifth ‘Delete’ button among 10 identical ones in Katalon Studio, you need to use an XPath locator with an index or target a unique parent element in the same row.
Solution 1: XPath with Index (Quick Fix)
Modify your Test Object’s selection method to use XPath with the index 5:
// In your Test Object, set Selection Method to "XPath"
// Use this XPath: (//button[@class='delete-btn'])[5]
WebUI.click(findTestObject('Page_Dashboard/btn_DeleteFifth'))
The parentheses around the XPath and the [5] at the end ensure Katalon clicks the 5th matching element, not the first.
Solution 2: Target Unique Parent Element (More Reliable)
If the Delete buttons are in a table with rows, use a unique identifier from that row (like an ID, name, or email) instead of index:
// Example: Target the Delete button in the row containing "John Doe"
// XPath: //tr[td[contains(text(),'John Doe')]]//button[@class='delete-btn']
WebUI.click(findTestObject('Page_Dashboard/btn_DeleteJohnDoe'))
This approach is more stable because:
It doesn’t depend on button order (which might change if rows are added/removed)
It directly targets the specific data row you want
Solution 3: JavaScript Click (If Others Fail)
If the element still throws ElementNotVisibleException, use JavaScript to click:
WebElement element = WebUiCommonHelper.findWebElement(findTestObject('Page_Dashboard/btn_DeleteFifth'), 30)
WebUI.executeJavaScript("arguments[0].click()", Arrays.asList(element))
Which Should You Use?
Situation
Best Solution
Buttons in fixed order, no unique row data
XPath with index [5]
Buttons in table rows with unique data (name, ID)
Target unique parent element
Element hidden/overlapped despite correct locator
JavaScript click
Recommendation: If your Delete buttons are in table rows, use Solution 2 (unique parent) — it’s the most robust approach mentioned in the forum
Katalon’s TestObject allows you to use CSS Selector as its locator as well as XPath. If you are new to both of CSS Selector and XPath, and if you want to choose one to get started, I would recommend you to learn CSS Selector rather than XPath.
If you want to learn CSS Selector, please read the following article:
By wrapping an XPath in parentheses and appending an index, we can target any specific instance: css=(//button[text()='Delete'])[5] or xpath=(//a[contains(@class,'btn-delete')])[5]
Modify the Test Object:
Open your ‘Delete’ button Test Object in Object Repository.
Set the Selection Method to Attributes.
Add a property named xpath.
Set its value to: (//button[@string()='Delete' or text()='Delete'])[${index}]
Check the Detect object by? box for this xpath property.
To keep your test scripts readable and reusable across your framework, wrap this logic into a Custom Keyword.
package com.architecture.keywords
import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.testobject.ObjectRepository
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
public class WebTableUtils {
/**
* Clicks an element from a list of identical elements based on its 1-based index.
* @param repositoryObjectId The path of the Test Object in the repository (e.g., 'Page_Dashboard/btn_Delete')
* @param index The 1-based position of the element you want to click (e.g., 5)
*/
@Keyword
def clickElementByIndex(String repositoryObjectId, int index) {
// Find the base test object from the repository
TestObject baseObject = ObjectRepository.findTestObject(repositoryObjectId)
// Dynamically pass the index parameter to the parameterized XPath
TestObject indexedObject = WebUI.convertTO(baseObject)
indexedObject.addProperty("xpath", com.kms.katalon.core.testobject.ConditionType.EQUALS, "((//button[text()='Delete'])[${index}])")
WebUI.comment("Attempting to click the element at index: " + index)
WebUI.waitForElementVisible(indexedObject, 10)
WebUI.click(indexedObject)
}
}
How to use it in your Script View:
Once the keyword is saved, you can cleanly call it in your Test Case script like this:
// Clicks the 5th delete button safely
CustomKeywords.'com.architecture.keywords.WebTableUtils.clickElementByIndex'('Object Repository/Page_Dashboard/btn_Delete', 5)
This keeps your repository clean with only one Test Object, ensures your script remains readable, and allows you to target the 1st, 5th, or 10th button effortlessly.
I’d also avoid choosing the button only by index unless the row order is very stable. If each row has some unique text, ID, email, title, or status, it’s usually safer to build the XPath around that row and then click the Delete button inside it. That way the test still clicks the right item even if a new row gets added above it.
Index works fine for quick cases, but for regression tests I’d rather target the row first, then the action button inside that row. It makes the test easier to trust later.
use an XPath with parentheses and an index, like (//button[text()='Delete'])[5]. The parentheses are key, without them the [5] won’t grab the 5th match globally.
indexing by position breaks easily though. If these buttons are in table rows, target the row first with unique text instead: //tr[td[contains(text(), 'SomeUniqueValue')]]//button[text()='Delete']
if you get ElementNotVisibleException, the button might be hidden behind something. Force a click with JavaScript:
WebElement el = WebUiCommonHelper.findWebElement(findTestObject('YourObject'), 10)
WebUI.executeJavaScript("arguments[0].click()", Arrays.asList(el))