Can't store a list of elements to create assertions

Hello everyone !

Here is what I’m trying to do :

1 - Catch all the elements that matches the following xpath : "//div[@class='price']/div[@class='pricenew'] | //div[@class='price']/div[@class='priceold'] | //div[@class='price']/div[@class='oneprice']"

2 - With that done and stored in an ArrayList variable , I wanna iterate through it and perform a check on each item to see if they have the correct symbol . For that , I have created a Keyword

 @Keyword
	def checkElements(ArrayList<WebElement> list, String currency) {
		for (WebElement price : list) {
			String priceItem = WebUI.getText(list(price))
			if (WebUI.verifyElementText(priceItem, currency)) {
				continue
			} else {
				System.println("This item doesn't have the correct currency symbol")
			}
		}
	}

The thing is : I have tried using findElements , I have tried using findTestObject , but none of them works (It doesnt get the elements and store in an array)

My code look as following right now :

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

import org.openqa.selenium.By
import org.openqa.selenium.WebDriver
import org.openqa.selenium.WebElement

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

WebUI.openBrowser('https://automationteststore.com/')

WebUI.maximizeWindow()


WebDriver driver = DriverFactory.getWebDriver()


List<WebElement> listPrices = driver.findElements(By.xpath("//div[@class='price']/div[@class='pricenew'] | //div[@class='price']/div[@class='priceold'] | //div[@class='price']/div[@class='oneprice']"))


CustomKeywords.'checkElement.checkElements'(listPrices, '$')

WebUI.enhancedClick(findTestObject('Home_Page_Elements/dropdownToggle'))

WebUI.enhancedClick(findTestObject('Home_Page_Elements/dropdownPound'))

CustomKeywords.'checkElement.checkElements'(listPrices, '£')

WebUI.enhancedClick(findTestObject('Home_Page_Elements/dropdownEuro'))

CustomKeywords.'checkElement.checkElements'(listPrices, '€')

WebUI.enhancedClick(findTestObject('Home_Page_Elements/dropdownDollar'))

CustomKeywords.'checkElement.checkElements'(listPrices, '$')

The error I keep getting is :

Caused by: groovy.lang.MissingMethodException: No signature of method: java.util.ArrayList.call() is applicable for argument types: (org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement) values: [[[CChromeDriver: chrome on WINDOWS (28974eee30b54427ffbfcfa46388ac70)] -> xpath: //div[@class='price']/div[@class='pricenew'] | //div[@class='price']/div[@class='priceold'] | //div[@class='price']/div[@class='oneprice']]]

Can anyone help me? Thanks !

What happens when you just try to combine the divs by the class instead of the union?

Also, your code would be easier to review if you put 3 backticks (like ``` ) on a row by themselves above your code and 3 backticks on a row below your code. The backtick is found on the same key as the tilde ( ~ ) in the upper left of the keyboard.

Maybe like:
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject

import org.openqa.selenium.By as By 
import org.openqa.selenium.WebDriver as WebDriver 
import org.openqa.selenium.WebElement as WebElement 

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

WebUI.openBrowser(‘https://automationteststore.com/’)

WebUI.maximizeWindow()

WebDriver driver = DriverFactory.getWebDriver()

List<WebElement> listPrices = driver.findElements(By.xpath("//div[@class='price']/div[@class='pricenew' or @class='priceold' or @class='oneprice']"))

CustomKeywords.'checkElement.checkElements'(listPrices, '$')

WebUI.enhancedClick(findTestObject('Home_Page_Elements/dropdownToggle'))

WebUI.enhancedClick(findTestObject('Home_Page_Elements/dropdownPound'))

CustomKeywords.'checkElement.checkElements'(listPrices, '£')

WebUI.enhancedClick(findTestObject('Home_Page_Elements/dropdownEuro'))

CustomKeywords.'checkElement.checkElements'(listPrices, '€')

WebUI.enhancedClick(findTestObject('Home_Page_Elements/dropdownDollar'))

CustomKeywords.'checkElement.checkElements'(listPrices, '$')

I actually added the backticks but somehow it didnt work as intended.

I tried your “way” and its giving the following error :

Caused by: groovy.lang.MissingMethodException: No signature of method: java.util.ArrayList.call() is applicable for argument types: (org.openqa.selenium.support.events.EventFiringWebDriver$EventFiringWebElement) values: [[[CChromeDriver: chrome on WINDOWS (d7da09f8894c95c449c2a8593278a7fb)] -> xpath: //div[@class='price']/div[@class='pricenew' or @class='priceold' or @class='oneprice']]]

We may need to see the HTML. In the meanwhile, keeping on the same tact, how about:

driver.findElements(By.xpath('//div[@class="price"]/div[contains(@class,"price")]'))

Same error =(

The url that I’m trying to create the test is https://automationteststore.com/

Thanks for the help !

Okay, so I’m running your code and it’s stopping at your checkElements() subroutine, not on your findElements() like I thought.

Yes, because the subroutine uses the list and a string as input to run the assertion.

Your keyword has problems:

    @Keyword
	def checkElements(ArrayList<WebElement> list, String currency) {
		for (WebElement price : list) {
			String priceItem = WebUI.getText(list(price))
			if (WebUI.verifyElementText(priceItem, currency)) {
				continue
			} else {
				System.println("This item doesn't have the correct currency symbol")
			}
		}
	}

Should rather be:

import org.openqa.selenium.WebElement

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

public class checkElement {
	@Keyword
	def checkElements(ArrayList<WebElement> list, String currency) {
		for (WebElement we : list) {
			String priceItem = we.getText()
			if (priceItem.contains(currency)) {
				continue
			} else {
				System.println("This item doesn't have the correct currency symbol")
			}
		}
	}
}

Your method checkElement accepts a list of org.openqa.selenium.WebElement as argument.

But WebUI.getText(xxxxx) can not accept that type.

WebUI.getText(xxxxx) expects an instance of com.kms.katalon.core.testobject.TestObject as argument.

You should be careful about the type (Fully Qualified Class Name).

1 Like

You missed the point.
Your Test Case has got the list of WebElements you want. It’s fine.
But your Keyword has problems.

1 Like

OOOOOH ! I mixed Katalon with Selenium then , thanks for that !

Now its running the keyword perfectly.

Great! Create an answer on here for that!

1 Like

The only thing I had to add was an assertion false in order to fail the test once the currency is not being shown as inteded.

The code now look as following :

@Keyword
	def checkElements(ArrayList<WebElement> list, String currency) {
		for (WebElement we : list) {
			String priceItem = we.getText()
			if (priceItem.contains(currency)) {
				continue
			} else {
				System.out.printf("The following item %s doesn't have the correct currency symbol\n",priceItem)
				assert false
				continue
			}
		}
	}

You can rewrite it using KeywordUtil:

import org.openqa.selenium.WebElement

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

public class checkElement {

	@Keyword
	def checkElements(List<WebElement> list, String currency) {
		for (WebElement we : list) {
			String priceItem = we.getText()
			if (! priceItem.contains(currency)) {
				KeywordUtil.markFailed("The following item ${priceItem} doesn't have the correct currency symbol")
			}
		}
	}
}
2 Likes

An alternative implementation of your test case:

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

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

WebUI.openBrowser('https://automationteststore.com/')

WebUI.maximizeWindow()

String xpath = """
		//div[@class='price']/div[@class='pricenew'] | 
		//div[@class='price']/div[@class='priceold'] | 
		//div[@class='price']/div[@class='oneprice']
	"""
TestObject tObj = makeTestObject(xpath)

List<WebElement> listPrices = WebUI.findWebElements(tObj, 10)

assert listPrices != null
assert listPrices.size() > 0

CustomKeywords.'checkElement.checkElements'(listPrices, '$')

WebUI.closeBrowser()

/**
 * create an instance of TestObject
 */
TestObject makeTestObject(String xpath) {
	TestObject tObj = new TestObject(xpath)
	tObj.addProperty("xpath", ConditionType.EQUALS, xpath)
	return tObj
}

Less “selenese”, more “katalonese”.

1 Like

That’s nice ! I’m really used to selenium , that’s why I created my script based on that.

I’m trying to create an assertion for another scenario here, which the HTML looks as following

Do you have any idea on how can I do it? I have tried like one “big” string but it fails , I can’t understand what the <br> tag does with the selector at this point.

Explaining better : This is a test to register a user, I just wanna assert that if the person tries to create the account without filling mandatory fields it’s going to throw this error on the redbox, and then I want to check if the messages that appear there are correct to the fields that were not filled.

Thank you !

How about using verifyTextPresent for each of the messages or as many as needed? Lastly, I take a screen shot of the page to see if any of the messages get changed between revisions, such as punctuation being removed or altered.

WebUI.verifyTextPresent("Login name must be alphanumeric only...", false)
WebUI.verifyTextPresent("First Name must be between...", false)

WebUI.takeScreenshot(gReportPathway + "LoginPage9.png")

That doesnt seem to be the “best” approach, right?
Creating a verifyTextPresent for each sentence

Doesn’t it have another way to do that?

There is also another way to check if the text in the Alert box at the top matches the required fields that has not been filled in.

Unfortunately because the Alert box text does not match the order of the input fields, I had to hard code the order in which it checks the input field alert text.

You can add this method to the very bottom of your test case or create a custom keyword for it

def checkRequiredFields() {
	//Set order of alert box text to the order of the required fields
	Integer[] rows = [12, 0, 1, 2, 6, 8, 10, 9, 13, 14]
	
	//Get the text from the Alert Box
	String actualAlert = WebUI.executeJavaScript('return document.querySelectorAll(".alert.alert-error.alert-danger")[0].innerText', []).substring(2).replaceAll('\n', ' ')
	
	//Create List to store required field text
	List alerts = []
	
	//Iterate over required fields and add the required field text to the list
	rows.each {it ->
		try {
			String inputAlert = WebUI.executeJavaScript('return document.querySelectorAll("div.form-group")[' + it.toString() + '].querySelectorAll("span.help-block")[0].innerText', [])
			if(inputAlert != '') {
				alerts.add(inputAlert)
			}
		}catch(Exception ex) {
			//Catch expection
		}
	}
	
	//Join the List into a single string
	String expectedAlert = alerts.join(' ')
	
	//Check if expected string matches the alert box text
	WebUI.verifyMatch(actualAlert, expectedAlert, false)
}

Then each time you want to check if the alert text from the fields match the alert text in the alert box, you just have to call the method:

checkRequiredFields()

So by using that method it will check that the alert box text is correct no matter what required fields were filled in or not according to the alert text of each field that was left out.

Here is an example of how it can be implemented:

2 Likes

Oh lord, that’s awesome !

Can you explain to me how is it capturing each row of that red alert box? Or its getting directly from the alert below the input boxes?

In the method there is a section that stores the text in the alert box at the top

You can paste this in your devtools console and see what the original string looks like for the alert text box:

document.querySelectorAll(".alert.alert-error.alert-danger")[0].innerText

I then take that string and remove the x in the start and replace all the \n characters with a blank space.
The \n character is a line break, which is also those <br> tags you see

Here is the output of the original string in devtools:

image

2 Likes