Working with Tables using the Selenium WebDriver and Katalon Studio

While Katalon is capable of reading a table through an object declaration, it’s possible to do the same using the “Webdriver”, “List” and “findElement” commands. I use this method to count the number of rows in a dynamically generated table. It would be equivalent to the storeXPathCount from the Selenium IDE.

Many of the tables I work with are static, meaning the number of rows doesn’t change. For example, we have a table for months, that’s always 12. We have a table for categories, that’s always 10. However, we have a table for customers, and that’s always different.

I have a couple of tests where I need to know the number of customers in that table. To find out, I use the Webdriver to create a List of table rows, then get the Size of that List.

I’ve also used this method when working with Contacts. We don’t display a count for the number of Contacts, but I can use the WebDriver to get a count of the objects I’m interested in, make a new one, then count them again to make sure it’s increased by one.

These commands come from standard Selenium and can be found in many useful examples. To get started we need a few new import statements.

import com.kms.katalon.core.webui.driver.DriverFactory as DriverFactory
import org.openqa.selenium.By as By
import org.openqa.selenium.WebElement as WebElement
import com.kms.katalon.core.annotation.Keyword as Keyword

Additional statements for logging would be:

import com.kms.katalon.core.logging.KeywordLogger as KeywordLogger

KeywordLogger log = new KeywordLogger()

Once those are declared, define the “Webdriver”

WebDriver driver = DriverFactory.getWebDriver()

With those in place, I can use the following code to count the number of items returned in my List.

WebElement Webtable = driver.findElement(By.id('sales_dash_table')) // Replace TableID with the Actual Table ID or Xpath reference
//Get the number of rows in the table and turn it into a List
List<WebElement> TotalRowCount = Webtable.findElements(By.xpath('//*[@id=\'sales_dash_table\']/tbody/tr'))
//Display the number of rows in the table for the given sales rep
log.logWarning('No. of Rows in the WebTable: ' + TotalRowCount.size())
//Loop through the table and output the information to Log File
//Read columns 1-8, assign each to a variable, then output the result to the Log File
for (int loop = 1; loop <= TotalRowCount.size(); loop++) {
 customerName= driver.findElement(By.xpath("//*[@id='sales_dash_table']/tbody/tr[" + (loop)+ "]/td[1]")).getText();
 log.logWarning('Customer Name:=' + customerName)
 sales = driver.findElement(By.xpath("//*[@id='sales_dash_table']/tbody/tr[" + (loop)+ "]/td[2]")).getText();
 log.logWarning('Sales Figure Amount:=' + sales)
 plan = driver.findElement(By.xpath("//*[@id='sales_dash_table']/tbody/tr[" + (loop)+ "]/td[3]")).getText();
 log.logWarning('Plan Value:=' + plan)
}

In my example, the WebElement is set to the variable WebTable, which corresponds to the ID “sales_dash_table”. The table I’m looking for has an ID of “sales_dash_table” defined within the CSS code. I want to look for that table, then count the number of rows in that table that are returned with the List command.

The By.xpath(“//*[@id=’sales_dash_table’]/tbody/tr” is the actual definition I want to count, in this case, the TR[].

The //* is a wildcard for all the div commands that are in front of the definition.

This is the standard Selenium method for manipulating a table and it works just the same in Katalon. In most cases I’m able to use an object definition, but when the table has an unknown length, it can be easily traversed with the WebDriver.

Other articles of interest:

Wait For Alert, Verify Alert Present in Katalon Studio

We’ve looked at waiting for an element to appear, so now we’ll look at waiting for a an alert to appear. For the site I’m working with, there are two alerts to check for. The first is a confirmation that the Prospect has been created correctly and appears every time the Prospect details are Saved.

The second is a warning dialog that presents when there is an error retrieving results. This is a search/query error and indicates a data error. For most search types this will not appear, but when it does, it means something is wrong.

Katalon has two commands to handle both of these:

WebUI.waitForAlert
WebUI.verifyAlertPresent

In most cases, either command will be followed by an:

WebUI.acceptAlert()

Which clicks OK for the dialog and continues executing code.

For my tests, I have mainly used waitForAlert. For the first scenario, I wait for the alert because I know it’s coming. For the second, I check to see if it pops up, and if not, continue on with the test.

After creating the Prospect, I wait for the confirmation dialog to appear. Once it does, I confirm it contains the “success” text. If it does, I log that and pass the test. If it contains something else, I accept the alert, but log an error as the result is unexpected.

//After the Prospect is created, a confirmation dialog is presented. This Accepts that dialog and allows the script to continue.
//Confirm the text is a Success message
elementPresent=WebUI.waitForAlert(20)
if (elementPresent==true) {
 alertText = WebUI.getAlertText()
 log.logWarning('The title of the alert is:=' + alertText)
 if (alertText=='Successfully Created Contact'){
 KeywordUtil.markPassed('SUCCESS: Successfully Created the Prospect')
 WebUI.acceptAlert()
 } else {
 WebUI.acceptAlert()
 KeywordUtil.markFailed('ERROR: There was an error creating the Prospect')
 }
}

The script waits 20 seconds for the confirmation dialog to appear. That seems like a lot, but in the QA environment, things take a little longer. The default is 30, so it saves a few seconds.

Once the dialog presents, the getAlertText gets the text of the message. This is compared to the success message I expect. If they match, write a success message to the log, mark the test as passed and click OK on the alert.

If the text is something else, mark the test as failed with an error message in the log file. It doesn’t really matter what the message was, it wasn’t a success, so this test needs manual follow up.

The code for the second scenario is almost identical as it is for the first. It’s more about it’s placement than anything else. In the first case, the alert check is at the end, when I know it will come up. The second is located earlier, right after I search for the product name. If the alert presents itself, the test is marked as failed and exits. If the alert doesn’t appear, the test continues as normal.

//Click the product name and pause to make sure a query error isn't displayed.
WebUI.click(findTestObject('Page_/Search Inventory/Search - Returned Product Name'))
 elementPresent=WebUI.waitForAlert(5)
 if (elementPresent==true) {
 alertText = WebUI.getAlertText()
 log.logWarning('The title of the alert is:=' + alertText)
 if (alertText=='There was an error retrieving results'){
 WebUI.acceptAlert() 
 KeywordUtil.markErrorAndStop('ERROR: There was an error retrieving results, output may not be complete.') 
 }
}

The script waits 5 seconds for the error to appear. If it does, the script confirms the error of the alert and writes that to log file. If it’s an error with the results, the dialog is dismissed, the test is marked as an error and execution stops.

If the error doesn’t appear the rest of the code continues, which is to verify details about the item I searched for.

Alerts can be tricky business, but Katalon has a few ways of handling them. My dialogs are pretty simple and straightforward and waitForAlert has served me well.

Other articles of interest:

Waiting for Elements to appear in Katalon Studio

As tests are running, there is a frequent need to pause the code execution to wait for an item to be available on the page. There might be a pause due to network speed or waiting for the results of a query. While it’s possible to use the Delay command, it might be better to use the WaitForElementVisible command.

This waits for the specific element you want to interact with to appear on the page before the test continues. For one of my tests, I need to wait for the number of results to be returned before moving on. I could wait for the page, but I specifically need this number, and this text doesn’t appear until the query has completed.

search-results-found

//Wait for the Results Found text to appear. This contains the number of Prospects on the page.
WebUI.waitForElementVisible(findTestObject('Page_/Prospect Page Objects/label-Prospect Results Found'), 10)

The code above waits for the label to appear, then my test can continue, which is to read that value.

You can couple waiting for an item to appear, with waiting for an item to have specific text. Katalon offers, verifyTextPresent, which waits until a specific block of text appears on the page. For example:

WebUI.verifyTextPresent("Month To Date Sales Detail", false)

Will wait for the words, “Month To Date Sales Detail” to appear before moving it. My reason for using this particular verification is, if that text doesn’t appear, the page didn’t load correctly so the next steps would be invalid.

//Look for the text, Month To Date Sales Detail on the Page.
//If it's not there, there is a problem loading the page and the test should exit as the rest of the steps will fail
WebUI.click(findTestObject('Page_/Sales Dashboard/Monthly Details/Monthly Sales Accordion'))
try {
 elementPresent=WebUI.verifyTextPresent("Month To Date Sales Detail", false)
}
catch (Exception e) {
 title = WebUI.getWindowTitle()
 log.logWarning('ERROR: The title of the Sales Detail Page is:=' + title)
 throw new AssertionError('ERROR: The Sales Detail Page did not load correctly', e)
}

This is a Try/Catch example, where the first step is to click to open a dashboard. It then checks if the header of the page has the text, “Month To Date Sales Detail”. If that is missing, the page is in error and the test needs to exit.

Further, if that page doesn’t appear, we’ve got a more serious problem to look at. Because of that problem, the test throws an AssertionError and marks the test as failed.

This could have been done using the MarkFailed command discussed previously. At the time it was a reasonable solution, so I’ve kept it. When I get around to refactoring this test, I will most likely change it to a VerifyElementPresent and use the method below.

Instead of the Try/Catch, I used VerifyElementPresent to determine which tabs are available for a user and run the appropriate test. The tabs available could be Contacts, Notes, Tasks and a few others.

profile-tabs

//Determine which of the tabs is visible and run the appropriate test
elementVisible=WebUI.verifyElementPresent(findTestObject('Page_/Customer Profile/tab-Contacts'), 5, FailureHandling.CONTINUE_ON_FAILURE)
if (elementVisible==true){
log.logWarning('--- Contacts tab is available, running test ---')
WebUI.callTestCase(findTestCase('Customer Profile/Contacts'), [:], FailureHandling.CONTINUE_ON_FAILURE)
} else {
 log.logWarning('--- Contacts tab is not available for this customer. No test to execute ---')
}

A new parameter, FailureHandling.CONTINUE_ON_FAILURE) is added at the end. It still waits 5 seconds for the object to appear, but, the main test case will continue if it’s not there. This is because I’ve stacked several CallTestCase commands within a single test. If the tab is available, the test case is called to verify the contents of that tab. If the tab doesn’t exist, a log entry is made, but the test moves on to check for the next tab.

I could make five separate tests and let each fail because the tab isn’t there, but this is more dynamic and reacts to the available elements on the page. If the tab is missing, an error will be flagged, but the test doesn’t stop dead.

The Delay command is useful to get a script to wait a determined amount of time before moving on. Some say it’s not the best practice, but it does work. However, when you need to wait for a particular item to appear, waitForElementVisible, verifyElementPresent, verifyTextPresent can be for more effective and reliable.

Other articles of interest:

New Libraries to Import into Katalon Studio

Up to this point, we have added several new import statements to our projects. One was to use SendKeys, another for writing to the log file and another to mark a test as Failed or in Error. There’s also a few more coming as we get into using the WebDriver, so to keep things up to date, here is a list of import statements to add to your project.

import internal.GlobalVariable as GlobalVariable
//This section is needed to call the Selenium WebDriver
import org.openqa.selenium.WebDriver as WebDriver
import com.kms.katalon.core.webui.driver.DriverFactory as DriverFactory
import org.openqa.selenium.By as By
import org.openqa.selenium.WebElement as WebElement
import com.kms.katalon.core.annotation.Keyword
//This allows for the use of MarkFailed and MarkError
import com.kms.katalon.core.util.KeywordUtil
//This is for SendKeys
import org.openqa.selenium.Keys as Keys
//This is to write to the log file
import com.kms.katalon.core.logging.KeywordLogger as KeywordLogger

KeywordLogger log = new KeywordLogger()

Other articles of interest:

Marking tests as Passed, Failed or in Error using MarkFailed, MarkPassed, MarkError in Katalon Studio

We previously looked at the idea of writing information to the log files. This included the output of variable values and status messages. But, what if you need to stop the test and mark it as failed because the data you needed wasn’t there, or the information you did read indicates a more serious problem? Katalon Studio will mark a test as failed if the command fails to execute, but there is a way to fail the test because of other errors. Again, it takes a little digging to find the right command, but they certainly exist.

To start, an import statement is needed at the top of the project:

import com.kms.katalon.core.util.KeywordUtil

Once that’s in place, a test can be flagged in several ways using one of these commands:

KeywordUtil.markPassed
KeywordUtil.markFailed
KeywordUtil.markError
KeywordUtil.markWarning

* https://api-docs.katalon.com/studio/v4.6.0.2/api/com/kms/katalon/core/util/KeywordUtil.html

With these KeywordUtil commands, you have a great deal of flexibility in controlling the execution of your tests. For this small snippet, the two values I’m working with are written to the log file. I then use KeywordUtil to mark the test as passed or failed if those two values match or not.

log.logWarning('The All filter displays: ' + filterAllUsers)
log.logWarning('The number of users for Pagination displays: ' + footerAllUsers)
if(filterAllUsers==footerAllUsers){
 KeywordUtil.markPassed('SUCCESS: The Filter Results Matches the Pagination Results')
} else {
 KeywordUtil.markFailed('ERROR: The Filter Results Does NOT Match the Pagination Results')
}

The test itself will pass since there is no error in the code or in finding the elements on the page. However, if the values I’m comparing aren’t equal, I need to fail the test so I can look at it manually and determine the problem.

As another example, I need to check one of our sales dashboards. Due to some data inconsistencies during import, it’s possible for the month to display twice on the page, as in January, January, February, February.

Verifying these duplicate values won’t get the test to fail, the code executes correctly. But, now I can force it to error when I evaluate a duplicated month.

//Count the number of months in the table. If there are more than 12, there's an error with duplication
log.logWarning('Number of Months displayed in the Table: ' + TotalRowCount.size())
if (TotalRowCount.size() > 12){
 log.logWarning('There are more than 12 months. This is an error.')
 KeywordUtil.markError('ERROR: There are more than 12 months. This is an error.')
}

//If the above passes, such as an onboarded user, check that the text of the first month isn't the same as the second
//Compare the first two months, display the first three for visual comparison
monthName1 = driver.findElement(By.xpath("//*[@id='byMonth']/div/table/tbody/tr[1]/td[1]")).getText();
log.logWarning('Month:=' + monthName1)
monthName2 = driver.findElement(By.xpath("//*[@id='byMonth']/div/table/tbody/tr[2]/td[1]")).getText();
log.logWarning('Month:=' + monthName2)
monthName3 = driver.findElement(By.xpath("//*[@id='byMonth']/div/table/tbody/tr[3]/td[1]")).getText();
log.logWarning('Month:=' + monthName3)
if (monthName1==monthName2){
 log.logWarning('The two months sampled have the same text. The months appear to be duplicated.')
 KeywordUtil.markError('ERROR: The two months sampled have the same text. The months appear to be duplicated.') 
}

Again, I’m using both log.LogWarning and KeywordUtil.markError to output values and then mark the test as in error.

A test can be passed or failed for almost any reason you can control. As another example, here is a snippet of code that indicates whether a Contact was created successfully if the popup dialog indicates there was a success.

elementPresent=WebUI.waitForAlert(20)
if (elementPresent==true) {
 alertText = WebUI.getAlertText()
 log.logWarning('The title of the alert is:=' + alertText)
 if (alertText=='Successfully Created Contact'){
 KeywordUtil.markPassed('SUCCESS: Successfully Created the Prospect')
 WebUI.acceptAlert()
 } else {
 WebUI.acceptAlert()
 KeywordUtil.markFailed('ERROR: There was an error creating the Prospect')
 }
}

After the contact is created, the code waits for an alert to display. For that alert, read the text. If the text is “Successfully Created Contact” then obviously all is well. If it reads as something else, then a problem has occurred and the test will be marked as Failed so I can review it later.

It’s also possible to brute force end the execution of the test using the throw command. The following will stop the test in it’s tracks and log an error:

throw new AssertionError('ERROR: There was an error creating the Prospect')

As noted before, the count of Passed, Failed and Error tests is displayed on the Log Viewer.

Katalon offers a huge amount of flexibility with the inclusion of KeywordUtil.markFailed and KeywordUtil.markError. It’s now possible to indicate there is a problem even when all the commands execute successfully. Or, you can exit out gracefully if you know the information you just read would cause the rest of the test to fail. An ideal case would be reading the value $0, right before you perform a calculation.

Other articles of interest:

Recent Comments