How handle a dynamic webtable in katalon studio. what are the ways we can access the table

In a dynamic table i want to fetch the data . Give me the example code for that . should i use List of List, or anyother way?

1 Like

Have a read below

1 Like

ya this is to fetch the single value based on one for loop,if suppose i have browsers name and its memory and cpu value. here i want to get the cpu value of chrome broswer. Then how could be the code?

You haven’t shown us any HTML that would really assist us to aid you, but I would look at getting the information as a List of Web Elements, like:

List<WebElement> cpuValues = driver.findElements(By.xpath('//table/tbody/...'))

Now, the variable, cpuValues, can be used to get the text.

for (int icnt = 0; icnt < cpuValues.size(); icnt++) {
    println("The values are: ${cpuValues.get(icnt).getText()}")
}

Note: if you are using Katalon Studio, use CTRL + SHIFT + O (oh) to have all the “import” statements added to your code.

Edit: And if you need additional assistance getting dynamic web tables column and row:

Edit2: Is there a public URL that we can visit to see this “dynamic webtable”?

Dynamic Tables page for Automation Testing Practice this is the dynamic table i want to automate. I need to fetch the value of Chromes CPU or Memory or Both like CPU and Memory. What are the ways to automate this.

The following will give you the names of the browsers.

xpath = id('core')//table/tbody/tr/td[1]

If you know how to display the HTML, you can copy the below pathway to the Find box of the DevTools display and it will show:

image

And to display the browser names:

List<WebElement> cpuValues = driver.findElements(By.xpath("id('core')//table/tbody/tr/td[1]"))

for (int icnt = 0; icnt < cpuValues.size(); icnt++) {
    println("The values are: ${cpuValues.get(icnt).getText()}")
    WebUI.comment("The values are: ${cpuValues.get(icnt).getText()}")
}

What this does is get all the first column (that’s the <td>) of all rows (that’s the <tr>) of the body of your “dynamic” table into a list. To get an individual row, then you insert the position into the pathway, like to get the third row:

xpath = id('core')//table/tbody/tr[3]/td[1]

If you want the “cpu” information, that is in the second column, so you could use:

xpath = id('core')//table/tbody/tr/td[2]
Maybe like:

An example test case:

import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject

import org.openqa.selenium.By as By
import org.openqa.selenium.Keys as Keys
import org.openqa.selenium.WebDriver as WebDriver
import org.openqa.selenium.WebElement as WebElement
import org.openqa.selenium.support.ui.WebDriverWait as WebDriverWait
import org.openqa.selenium.support.ui.ExpectedConditions as ExpectedConditions

import com.kms.katalon.core.webui.driver.DriverFactory as DriverFactory
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

gSiteUrl = 'https://practice.expandtesting.com/dynamic-table'

WebUI.openBrowser(gSiteUrl )

// like it better when see all the items
WebUI.maximizeWindow()

WebDriver driver = DriverFactory.getWebDriver();

WebDriverWait wait = new WebDriverWait(driver, 10)

// move below the advertising so you can see your table
WebElement scenario = driver.findElement(By.xpath("id('core')"))
WebUI.scrollToPosition(300, scenario.getLocation().getY())
WebUI.delay(0.5)

"name of the browsers"
List<WebElement> browserValues = driver.findElements(By.xpath("id('core')//table/tbody/tr/td[1]"))

wait.until(ExpectedConditions.elementToBeClickable(browserValues.get(0)));

for (int icnt = 0; icnt < browserValues.size(); icnt++) {
	println("The values are: ${browserValues.get(icnt).getText()}")
	WebUI.comment("The values are: ${browserValues.get(icnt).getText()}")
}

"name of the cpu values"
List<WebElement> cpuValues = driver.findElements(By.xpath("id('core')//table/tbody/tr/td[2]"))

wait.until(ExpectedConditions.elementToBeClickable(cpuValues.get(0)));

for (int icnt = 0; icnt < cpuValues.size(); icnt++) {
	println("The values are: ${cpuValues.get(icnt).getText()}")
	WebUI.comment("The values are: ${cpuValues.get(icnt).getText()}")
}

"name of the memory values"
List<WebElement> memoryValues = driver.findElements(By.xpath("id('core')//table/tbody/tr/td[3]"))

wait.until(ExpectedConditions.elementToBeClickable(memoryValues.get(0)));

for (int icnt = 0; icnt < memoryValues.size(); icnt++) {
	println("The values are: ${memoryValues.get(icnt).getText()}")
	WebUI.comment("The values are: ${memoryValues.get(icnt).getText()}")
}

// do whatever you want below here; I'm just going to pause
WebUI.delay(10)

Edit: Just a reminder that the HTML start at 1 but the list of Web Elements start at 0.

We can’t use this(td[2]) because its dynamic whn we refresh cpu column could be 3rd or 4th

The source code is avaliable at


I would propose the following Test Case TC1:

import org.openqa.selenium.WebElement

import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

/**
 * TC1
 */

// The HTML of this URL contains a terrible <table>. 
// It changes the order of rows and columns
// every time the page is retrieved.
String url = 'https://practice.expandtesting.com/dynamic-table'
WebUI.openBrowser('')
WebUI.navigateToUrl(url)

// wait for the page is loaded on the browser viewport
WebUI.verifyElementPresent(makeTestObject('coreTable', 'id("core")//table'), 10)

// get the list of <th>Name</th>...<th>CPU</th>... as the column titles
List<WebElement> listOfThInThead = 
	WebUI.findWebElements(
		makeTestObject('listofTdInThead', 'id("core")//table/thead/tr/th'), 10)

// find the index of "Name" column
int nameColumnIndex = findIndexOfWebElementByText(listOfThInThead, 'Name')
println "nameColumnIndex=${nameColumnIndex}"

// find the index of "CPU" column
int cpuColumnIndex = findIndexOfWebElementByText(listOfThInThead, 'CPU')
println "cpuColumnIndex=${cpuColumnIndex}"

// get the list of <td>Chrome</td>...<td>Firefox</td>... as the browser names
List<WebElement> listOfTdInNameColumn = 
	WebUI.findWebElements(
		makeTestObject('listOfTdInNameColumn', "id('core')//table/tbody/tr/td[${nameColumnIndex + 1}]"), 10)

// find the index of "Chrome" row
int chromeRowIndex = findIndexOfWebElementByText(listOfTdInNameColumn, 'Chrome')
println "chromeRowIndex=${chromeRowIndex}" 

// select the <td> of "Chrome"x"CPU", get the content text
String textOfChromeCPU = 
	WebUI.getText(makeTestObject('chromeCPU', "id('core')//table/tbody/tr[${chromeRowIndex + 1}]/td[${cpuColumnIndex + 1}]"))
println "textOfChromeCPU=${textOfChromeCPU}"

WebUI.closeBrowser()

/*
 * 
 */
TestObject makeTestObject(String id, String xpath) {
	TestObject tobj = new TestObject(id)
	tobj.addProperty("xpath", ConditionType.EQUALS, xpath)
	return tobj
}

/*
 * 
 */
int findIndexOfWebElementByText(List<WebElement> list, String text) {
	int v = -1
	list.eachWithIndex { webElement, index ->
		if (webElement.getText() == text) {
			v = index
		}
	}
	return v
}

I know, the source of TC1 looks ugly. I will show you better variations later.

1st result:

nameColumnIndex=0
cpuColumnIndex=1
chromeRowIndex=3
textofChromeCPU=4.6%

2nd result:

nameColumnIndex=0
cpuColumnIndex=4
chromeRowIndex=0
textofChromeCPU=4.5%

3rd result:

nameColumnIndex=0
cpuColumnIndex=3
chromeRowIndex=0
textofChromeCPU=4.4%

In the results, you can find that the cpuColumnIndex and chromeRowIndex move. How terrible the subject <table> is!

1 Like

TC2

The TC1 can extract data of “Chrome-CPU” only. Next, I want to scrape the data of “Firefox-Memory” as well. How to do it?

In the TC2 introduces one more layer of abstraction: a function named
String getCellText(String browser, String metric).

import org.openqa.selenium.WebElement

import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

/**
 * TC2
 */

String url = 'https://practice.expandtesting.com/dynamic-table'
WebUI.openBrowser('')
WebUI.navigateToUrl(url)

// wait for the page is loaded on the browser viewport
WebUI.verifyElementPresent(makeTestObject('coreTable', 'id("core")//table'), 10)

// get the text of Chrome-CPU cell
String chromeCPU = getCellText("Chrome", "CPU")
println "Chrome-CPU=${chromeCPU}"


// get the text of Firefox-Memory cell
String firefoxMemory = getCellText("Firefox", "Memory")
println "Firefox-Memory=${firefoxMemory}"

WebUI.closeBrowser()

/*
 * 
 */
TestObject makeTestObject(String id, String xpath) {
	TestObject tobj = new TestObject(id)
	tobj.addProperty("xpath", ConditionType.EQUALS, xpath)
	return tobj
}

/*
 * 
 */
int findIndexOfWebElementByText(List<WebElement> list, String text) {
	int v = -1
	list.eachWithIndex { webElement, index ->
		if (webElement.getText() == text) {
			v = index
		}
	}
	return v
}

/*
 * 
 */
String getCellText(String browser, String metric) {
	println "\n>>> browser=${browser}, metric=${metric}"
	
	// get the list of <th>Name</th>...<th>CPU</th>... as the column titles
	List<WebElement> listOfThInThead =
		WebUI.findWebElements(
			makeTestObject('listofTdInThead', 'id("core")//table/thead/tr/th'), 10)
	
	// find the index of "Name" column
	int nameColumnIndex = findIndexOfWebElementByText(listOfThInThead, "Name")
	println "nameColumnIndex=${nameColumnIndex}"
	assert nameColumnIndex >= 0
	
	// find the index of the problem column
	int theColumnIndex = findIndexOfWebElementByText(listOfThInThead, metric)
	println "theColumnIndex=${theColumnIndex}"
	assert theColumnIndex >= 0
	
	// get the list of <td>Chrome</td>...<td>Firefox</td>... as the browser names
	List<WebElement> listOfTdInNameColumn =
		WebUI.findWebElements(
			makeTestObject('listOfTdInNameColumn', "id('core')//table/tbody/tr/td[${nameColumnIndex + 1}]"), 10)
	
	// find the index of the problem row
	int theRowIndex = findIndexOfWebElementByText(listOfTdInNameColumn, browser)
	println "theRowIndex=${theRowIndex}"
	assert theRowIndex >= 0
	
	String text = WebUI.getText(makeTestObject('theCellValue', "id('core')//table/tbody/tr[${theRowIndex + 1}]/td[${theColumnIndex + 1}]"))
	println "text=${text}"
	return text
}

TC2 calls this function twice with different set of arguments.

Please note that the getCellText function is located inside the TC2. Therefore the getCellText is available for the TC2 only; no other Test Case scripts can use it.

Externalizing the common functions

Next I want to make 2 seperated Test Case scripts. One script will scrape the Chrome-CPU data. Another script will scrape the Firefox-Memory data.

I want to avoid any code duplication. So, I created 2 Groovy classes in the Keywords folder:

practiceexpandtesting.DynamicTableScraper

package practiceexpandtesting

import org.openqa.selenium.WebElement

import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import my.TestObjectUtils

public class DynamicTableScraper {

	static String getCellText(String browser, String metric) {
		println "\n>>> browser=${browser}, metric=${metric}"

		// get the list of <th>Name</th>...<th>CPU</th>... as the column titles
		List<WebElement> listOfThInThead =
				WebUI.findWebElements(
				TestObjectUtils.makeTestObject('listofTdInThead', 'id("core")//table/thead/tr/th'), 10)

		// find the index of "Name" column
		int nameColumnIndex = findIndexOfWebElementByText(listOfThInThead, "Name")
		println "nameColumnIndex=${nameColumnIndex}"
		assert nameColumnIndex >= 0

		// find the index of the problem column
		int theColumnIndex = findIndexOfWebElementByText(listOfThInThead, metric)
		println "theColumnIndex=${theColumnIndex}"
		assert theColumnIndex >= 0

		// get the list of <td>Chrome</td>...<td>Firefox</td>... as the browser names
		List<WebElement> listOfTdInNameColumn =
				WebUI.findWebElements(
				TestObjectUtils.makeTestObject('listOfTdInNameColumn', "id('core')//table/tbody/tr/td[${nameColumnIndex + 1}]"), 10)

		// find the index of the problem row
		int theRowIndex = findIndexOfWebElementByText(listOfTdInNameColumn, browser)
		println "theRowIndex=${theRowIndex}"
		assert theRowIndex >= 0

		TestObject toCell = TestObjectUtils.makeTestObject('theCellValue', "id('core')//table/tbody/tr[${theRowIndex + 1}]/td[${theColumnIndex + 1}]")
		String text = WebUI.getText(toCell)
		println "text=${text}"
		return text
	}

	static private int findIndexOfWebElementByText(List<WebElement> list, String text) {
		int v = -1
		list.eachWithIndex { webElement, index ->
			if (webElement.getText() == text) {
				v = index
			}
		}
		return v
	}
}

my.TestObjectUtils

package my

import com.kms.katalon.core.testobject.ConditionType
import com.kms.katalon.core.testobject.TestObject

public class TestObjectUtils {

	static TestObject makeTestObject(String id, String xpath) {
		TestObject tobj = new TestObject(id)
		tobj.addProperty("xpath", ConditionType.EQUALS, xpath)
		return tobj
	}
}

and I created 2 test case scripts that call these Groovy classes.

TC3_Chrome-CPU

import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

import practiceexpandtesting.DynamicTableScraper as Scraper
import my.TestObjectUtils

/**
 * TC3_Chrome-CPU
 */

String url = 'https://practice.expandtesting.com/dynamic-table'
WebUI.openBrowser('')
WebUI.navigateToUrl(url)

// wait for the page is loaded on the browser viewport
WebUI.verifyElementPresent(TestObjectUtils.makeTestObject('coreTable', 'id("core")//table'), 10)

String text = Scraper.getCellText("Chrome", "CPU")
println "Name:Chrome, Metric:CPU, text:${text}"

WebUI.closeBrowser()

TC3_Firefox-Memory

import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

import practiceexpandtesting.DynamicTableScraper as Scraper
import my.TestObjectUtils

/**
 * TC3_Firefox-Memory
 */

String url = 'https://practice.expandtesting.com/dynamic-table'
WebUI.openBrowser('')
WebUI.navigateToUrl(url)

// wait for the page is loaded on the browser viewport
WebUI.verifyElementPresent(TestObjectUtils.makeTestObject('coreTable', 'id("core")//table'), 10)

String text = Scraper.getCellText("Firefox", "Memory")
println "Name:Firefox, Metric:Memory, text:${text}"

WebUI.closeBrowser()

These 2 test case scripts calls the external Groovy classes. Thus code duplication is minimized.

Custom Keyword

Finally, I want to edit a Test Case that calls the practiceexpandtesting.DynamicTableScraper class in the Manual view. How to make it possible?

I created a custom Keyword that indirectly calls the practiceexpandtesting.DynamicTableScraper class:

‘practiceexpandtesting.ScrapeDynamicTableKeyword’

package practiceexpandtesting

import com.kms.katalon.core.annotation.Keyword


public class ScrapeDynamicTableKeyword {

	@Keyword
	static String getCellText(String browser, String metric) {
		return DynamicTableScraper.getCellText(browser, metric)
	}
}

Please find that the @Keyword annotation is added here. With this annotation given, Katalon Studio now recognizes the practiceexpandtesting.ScrapeDynamicTableKeyword class as a custom keyword and support it in the Manual view of the Test Case editor. See

TC4_in_Manual_view

If you do not need your custom classes to be supported in the Manual view, you don’t need the @Keyword annotation at all.

The TC4 uses the custom keyword:


import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

import my.TestObjectUtils as TestObjectUtils

/**

* TC4

*/

String url = 'https://practice.expandtesting.com/dynamic-table'

WebUI.openBrowser('')

WebUI.navigateToUrl(url)

// wait for the page is loaded on the browser viewport

WebUI.verifyElementPresent(TestObjectUtils.makeTestObject('coreTable', 'id("core")//table'), 10)

String chromeCPU = CustomKeywords.'practiceexpandtesting.ScrapeDynamicTableKeyword.getCellText'('Chrome', 'CPU')

println("chrome,CPU=$chromeCPU")

WebUI.closeBrowser()