Looping and perform action in a changing Table

Hi there,

I followed this tutorial about how to Handle Web Table (thanks for this !)

https://docs.katalon.com/katalon-studio/docs/handle_web_tables.html#what-is-web-tables

I’m facing some issue because the action i’m performing on the table is to click on a checkbox of a specific row (founded with my ‘Expected value’) then click a button that delete that row in the table.
So the result of the table is changing and the test case crash after the first delete (certainly because the web element objet previously define as changed).

It seems to be a trouble with the position of the loop(s).

My goal is to delete each row when a specific columns match the ‘Expected value’ , but i have to read the table again after each delete. And i’m not really good at this :upside_down_face: !

Thanks for your help !

Katalon 7.9

Hi @Bailrod,

Hmmm I think you need to make your object general so that whenever the result of the table is changed the locator will adjust. Let me share this link to you. This approach might help when you are working on lists/tables

Hope that helps. . . :smiley:

Thanks for the reply !

I’ll try but it’s a bit hard to me to adapt it evene if i understand a little the meaning of that trick.

@Bailrod You also want to exit both inner and outer loops, so don’t forget when you match to expectedValue to “break Loop;” (assuming you put a label above your first for statement titled Loop:

@grylion54 actually my expected value could appear more than once. So if i break the loop it will only do the action for the first item right ?

I’m stuck and i don’t get it…i’m a bit noob !

I can have multiple line with my expected value at last column (“2nd”), whatever the row position, and they all have to be deleted.

In fact the only row that stay will be the row containing “1er” at the last column :

So here is one of the code i try to use . Actually it returns a StaleElementReferenceException confirming that the element that i’m refering to as changed or gone.

Thanks for the help !

WebDriver driver = DriverFactory.getWebDriver()

String ExpectedValue = '2nd'

'In Table'
WebElement benef_grid = driver.findElement(By.xpath('//div[@class="conteneur"]/table/tbody'))

'To locate rows of table it will Capture all the rows available in the table '
List<WebElement> Rows = benef_grid.findElements(By.tagName('tr'))

println('Nbr of rows: ' + Rows.size())

'Find a matching text in a table and performing action'

'Loop will execute for all the rows of the table'
for (int i = 0; i < Rows.size(); i++) {
	
	'To locate columns(cells) of that specific row'
List<WebElement> Cols = Rows.get(i).findElements(By.tagName('td'))

for (int j = 0; j < Cols.size(); j++) {
    'Verifying the expected text in the each cell'
    if (Cols.get(j).getText().equalsIgnoreCase(ExpectedValue)) {
			
        'Click the check box of that row then delete the row'
        Cols.get(0).click()

        WebUI.click(findTestObject('Sinistres/input_supprimer'))
    }
}
}
'Validate!'
//WebUI.click(findTestObject('Sinistres/input_enregistrer'))

WebUI.delay(60)

@Bailrod

Please save your target web page into a single MHTML file using Chrome DevTool, and share the file here.

In Chrome browser having your target HTML opened, if you right click on a page, in a drop down menu you can choose “Save as” which calls up a dialog where you can select to “save as, Webpage, Single file”. Do it and you will get a file. That’s a MHTML file.

If you could share the MHTML file, we will be able to reproduce your target HTML live on our PC without immediate internet connection to its URL. We will be able to hack for some solution.

Here it is benef_grid.7z|attachment (50.2 KB)

Thanks !

Or if it didn’t work you can download it here

@Bailrod I’m certain kazurayam will come back with a super solution for you. Mine is quite blasé. I only added the indicator to continue looping with your code until only 1 row is left in the table. I also added an indicator to prevent an infinite loop if you do not encounter your ExpectedValue in the table.

WebDriver driver = DriverFactory.getWebDriver()

String ExpectedValue = '2nd'
boolean isWorking = true;
boolean hasRemoved = false;

while (isWorking) {
	'In Table'
	WebElement benef_grid = driver.findElement(By.xpath('//div[@class="conteneur"]/table/tbody'))
	
	'To locate rows of table it will Capture all the rows available in the table '
	List<WebElement> Rows = benef_grid.findElements(By.tagName('tr'))
	
	println('Nbr of rows: ' + Rows.size())
	if (Rows.size() <= 1) {
		isWorking = false;
	} else {
	
		'Find a matching text in a table and performing action'
        Loop:
		for (int i = 0; i < Rows.size(); i++) {
			
			'To locate columns(cells) of that specific row'
			List<WebElement> Cols = Rows.get(i).findElements(By.tagName('td'))
		
			for (int j = 0; j < Cols.size(); j++) {
				'Verifying the expected text in the each cell'
				if (Cols.get(j).getText().equalsIgnoreCase(ExpectedValue)) {
					
					'Click the check box of that row then delete the row'
					Cols.get(0).click();
					
					hasRemoved = true;
					
					WebUI.click(findTestObject('Sinistres/input_supprimer'))
                    break Loop;
				}
			}
		}
		if (!hasRemoved) {
			isWorking = false;
		}
	}
}

@Bailrod

Thank you for sharing the MHTML file. That makes any studies much easier.

I have made a GitHub repository to propose my solution for you, see this:

You can down load the zip of the project at https://github.com/kazurayam/benef_grid/releases


Test Cases/TC1 shows my proposed solution. When I execute TC1, it emitted following output in the console this would tell you how it ran:

***************
 supprimerARow(driver, pattern=1er) was invoked
2021-02-04 11:10:49.450 INFO  c.k.k.c.keyword.builtin.CommentKeyword   - Number of rows: 3
cell(0,1)=ROLLAND, matchFound=false
cell(0,2)=RONY, matchFound=false
cell(0,3)=174 RUE DU LIEUTENANT MOISANT , matchFound=false
cell(0,4)=77190 , matchFound=false
cell(0,5)= , matchFound=false
cell(0,6)= , matchFound=false
cell(0,7)=Non réglé , matchFound=false
cell(0,8)=2nd, matchFound=false
cell(1,1)=DURAND, matchFound=false
cell(1,2)=CLARENCE, matchFound=false
cell(1,3)=CRIFO-MME DENION / BP 31528 37 BIS QUAI DE VERSAILLES BP, matchFound=false
cell(1,4)=44015 , matchFound=false
cell(1,5)= , matchFound=false
cell(1,6)= , matchFound=false
cell(1,7)=Non réglé , matchFound=false
cell(1,8)=2nd, matchFound=false
cell(2,1)=ADAM, matchFound=false
cell(2,2)=, matchFound=false
cell(2,3)=LUSTIG SARL 1 RUE AUGUSTE HIMLY, matchFound=false
cell(2,4)=67000 , matchFound=false
cell(2,5)=03 88 02 70 92 , matchFound=false
cell(2,6)=03 88 80 92 20 , matchFound=false
cell(2,7)=Non réglé , matchFound=false
cell(2,8)=1er, matchFound=true
Yah! I clicked the button to removed this row ------ the row is should be removed by JavaScript

Test Cases/TC0 is the mimic of your original code using the MHTML file as input.

What is the difference between the TC0 and TC1?

Your original code TC0 wants to do everything at once in a loop. This design is not good; easily broken, hard to read. You should design your code with the ancient paradigm Divide-and-conquer in mind.

TC1 introduces an internal function named supprimerARow() which tries to click the Supprimer button at most 1 time. And the TC1 script body calls the function multiple times until it finds all rows have been appropriately processed.

import java.nio.file.Path
import java.nio.file.Paths

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

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

int loopCount = 0
int MAX_REPEAT = 3    // exit infinite loop if the loopCount execeded the max

Path projectDir = Paths.get(RunConfiguration.getProjectDir())
Path mhtml = projectDir.resolve('benef_grid.mhtml')
String url = mhtml.toFile().toURI().toURL().toExternalForm()

WebUI.openBrowser('')
WebUI.navigateToUrl(url)
WebUI.delay(1)

WebDriver driver = DriverFactory.getWebDriver()
String expectedValue = '1er'

Boolean removed  = supprimerARow(driver, expectedValue); loopCount += 1
while (removed && loopCount <= MAX_REPEAT) {
	removed = supprimerARow(driver, expectedValue); loopCount += 1
}

WebUI.closeBrowser()



/**
 * try to find a row with text containing the expected value, and "click" amd "remove" it.
 * if you want to remove multiple rows in the table, call this function mutile times until
 * it returns 0.
 * 
 * @param driver instance of WebDriver
 * @param String the expected value. e.g. '2nd'
 * @return true if a row was found and "clicked"; false in case no row was found
 */
Boolean supprimerARow(WebDriver driver, String pattern) {
	println "***************\n supprimerARow(driver, pattern=${pattern}) was inveked"
	
	'the conteneur table'
	WebElement benef_grid = driver.findElement(By.xpath('//div[@class="conteneur"]/table/tbody'))

	'capture all the rows available in the table '
	List<WebElement> rows = benef_grid.findElements(By.tagName('tr'))
	WebUI.comment("Number of rows: ${rows.size()}")
	
	if (rows.size() > 0) {
		for (int rx = 0; rx < rows.size(); rx++) {
			List<WebElement> cols = rows[rx].findElements(By.tagName('td'))
			Boolean matchFound = false
			for (int cx = 1; cx < cols.size(); cx++) {
				if (cols[cx].getText().equalsIgnoreCase(pattern)) {
					matchFound = true
				}
				println "cell(${rx},${cx})=${cols[cx].getText()}, matchFound=${matchFound}"
				if (matchFound) {
					break
				}
			}
			'this row contains a text matching with the given pattern'
			if (matchFound) {
				'click the check box of the row'
				cols[0].click()
				println "Yah! I clicked the button to removed this row ------ the row is should be removed by JavaScript"
				
				'ensure the page gets stable'
				WebUI.delay(1)
				'1 row was found and processed'
				return true
			} else {
				;    'continue to process the following row'	
			}
		}
		'We found no row which contains text matching with the given pattern'
		return false
		
	} else {
		'zero row found'
		return false
	}
}



Note: the initial version of TC1 script went down into an infinite loop. In order for the code to exit from the infinite loop, I introduced a magical constant MAX_REPEAT = 3. @Bailrod, you can change this to MAX_REPEAT = 100 or something.

Why infinite loop? Because the web page reproduced from the MHTML file worked wrongly. The web page DOES NOT respond to UI interactions at all. Test Case fails to click the radiobutton. The “Supprimer” button does not respond when clicked.

Why the web page reproduced from MHTML works wrongly? ---- it is because Chrome browser exclude all javascript fragments when it saves the page into a MHTML file. Therefore the reproduced web pages looks similar to the original but is dumb; does not interact at all.

1 Like

Woooow, that works !! It’s doing the job whatever the row position of my expected values !

Many Many Many thanks @kazurayam !

And yes i’m a Katalon Entreprise user :wink:

You might find the TC1 runs very slowly. It is because it has several nested loops which emits very verbose Step Execution logs. It requires long seconds to flush the bulky messages into the Log Viewer GUI.

If you are an KS Enterprise user, you can toggle the Step Executio logging Off in the Project Settings > Execution dialog.

Even if you do not have the Enterprise license, still you have an option to make it quicker. See

@YoungNgo

let me remind you of this issue. I hope the option of toggling Step Execution Log to be available to all KS free version users. @Bailrod wrote to me:

This post might help, because i have a similar performance issue with a json array loop, converted into excel file and logging sooooooo much info that takes a lot of time !

1 Like