Cannot switch to desired web element

Hi all!

I have a script that calls another script to verify if the actual account is the one that is logged in, if not,switch to that account. THe script runs perfectly well when ran on its own. In the other script which calls this one, it fails on the second switch. Please see error below. I am wondering if this is because when i created the switch script the object’s properties were different than what they are now.
Is is because when you do a spy on an object, it uses xpaths that are specific to where you did spy the object from? And if you are in another part of the web page, it cannot find those properties and hence fail? I am confused as to why this is happening. Any comments? suggestions? Thanks.

Unable to find the element located by ‘By.xpath: .//*[normalize-space(text()) and normalize-space(.)=‘2.3_Train09’])[2]/following::div[4]’. Please recheck the objects properties to make sure the desired element is located.

import static com.kms.katalon.core.checkpoint.CheckpointFactory.findCheckpoint
import static com.kms.katalon.core.testcase.TestCaseFactory.findTestCase
import static com.kms.katalon.core.testdata.TestDataFactory.findTestData
import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject
import static com.kms.katalon.core.testobject.ObjectRepository.findWindowsObject
import com.kms.katalon.core.checkpoint.Checkpoint as Checkpoint
import com.kms.katalon.core.cucumber.keyword.CucumberBuiltinKeywords as CucumberKW
import com.kms.katalon.core.mobile.keyword.MobileBuiltInKeywords as Mobile
import com.kms.katalon.core.model.FailureHandling as FailureHandling
import com.kms.katalon.core.testcase.TestCase as TestCase
import com.kms.katalon.core.testdata.TestData as TestData
import com.kms.katalon.core.testobject.TestObject as TestObject
import com.kms.katalon.core.webservice.keyword.WSBuiltInKeywords as WS
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.windows.keyword.WindowsBuiltinKeywords as Windows
import internal.GlobalVariable as GlobalVariable

String integratorLink = “Object Repository/Page_Stratocast portal - Cloud administrators/IntegratorsLink”;
String oldIntegratorNameLink = “Object Repository/Page_Stratocast portal - Integrators/integratorName”
String backArrowIntegratorEditPage = “Object Repository/Page_Stratocast portal - Massimo2.3_Train09 - Edit/backArrow”
String integratorsTabLink = “Object Repository/Page_Stratocast portal - Massimo2.3_Train09 - Edit/a_Integrators”
String clientsLink = “Object Repository/Page_Stratocast portal - Client accounts/span_Clients”
String accountName = “Object Repository/Page_Stratocast portal - Client accounts/accountName”
String switchAccountsArrow = “Object Repository/Page_Stratocast portal - Cloud administrators/switchAccountsArrow”
//String configurationsButton = “Object Repository/Page_Stratocast portal - Dashboard/span_Configurations”
Boolean accountLoggedIn
TestObject configurationsButton = new TestObject(“Object Repository/Page_Stratocast portal - Dashboard/span_Configurations”)

WebUI.callTestCase(findTestCase(‘Admission Authority/Admission Authority - Login - Organizational Account (750)’), [:], FailureHandling.CONTINUE_ON_FAILURE)

//Switch accounts

accountLoggedIn = CustomKeywords.‘admissionAuthority.AdminLogin.getLoggedInAccount’(GlobalVariable.switchToCloudAdmin,accountLoggedIn)

WebUI.click(findTestObject(integratorLink))

CustomKeywords.‘integrators.Integrators.filterIntegrator’(GlobalVariable.oldIntegratorName)

WebUI.click(findTestObject(oldIntegratorNameLink))

//Remote monitoring setting
CustomKeywords.‘integrators.Integrators.remoteMonitoringSetting’(GlobalVariable.remoteMonitoringSetting1)

CustomKeywords.‘integrators.Integrators.scrollToElement’(switchAccountsArrow)

//Switch accounts

accountLoggedIn = CustomKeywords.‘admissionAuthority.AdminLogin.getLoggedInAccount’(GlobalVariable.switchToClientAccount,accountLoggedIn)

@Keyword
def getLoggedInAccount(String account,accountLoggedIn) {
‘this method is to verify which account is logged in and sets a flag to determine if the client account needs to be logged in’
accountLoggedIn = false
if (WebUI.verifyElementVisible(findTestObject(cloudAdminAccount),FailureHandling.OPTIONAL)){
KeywordUtil.markPassed(‘Cloud administrator is logged in’)
if (account != ‘Cloud administrator’) {
‘if we want the cloud administrator to be logged in, switch to that account’
WebUI.click(findTestObject(switchAccountsArrow))
WebUI.click(findTestObject(switchAccountsMenuOption))
new clientAccount.ClientAccount().getRowColumnTable(account)
return accountLoggedIn
}
}
else if (WebUI.verifyElementVisible(findTestObject(integratorAdminAccount), FailureHandling.OPTIONAL)){
KeywordUtil.markPassed(‘Integrator administrator is logged in’)
if (account != ‘Integrator administrator’) {
‘if we want the integrator administrator to be logged in, switch to that account’
WebUI.click(findTestObject(switchAccountsArrow))
WebUI.click(findTestObject(switchAccountsMenuOption))
new clientAccount.ClientAccount().getRowColumnTable(account)
return accountLoggedIn
}
}
else {
if (WebUI.verifyElementVisible(findTestObject(clientAdminAccount), FailureHandling.OPTIONAL)) {
KeywordUtil.markPassed(‘Client administrator is logged in’)
accountLoggedIn = true
//If client admin is the one that is not suppose to be logged in, switch accounts
if (account != ‘Client administrator’) {
‘if we want the client administrator to be logged in, switch to that account’
WebUI.click(findTestObject(switchAccountsArrow))
WebUI.click(findTestObject(switchAccountsMenuOption))
new clientAccount.ClientAccount().getRowColumnTable(account)
return accountLoggedIn = false
}
return accountLoggedIn
}
else {
KeywordUtil.markFailed(“Account " + account + " was not successfully logged in”)
}
}

In the ideal world I would hope not, because relative to a given web page (body/div/list…) things like xpath are relative to the body tag and shouldn’t change unless the page content changes (for example, a if the page refreshes and a list changes size).

THAT SAID, I am working with Windows applications, not web pages, and its a wild west free-for-all in that case, because the Xpath appears to be competely relative to the order the web elements are used (touched).

When I click on a a specific button during a recording session, the test object is generated, and the Xpath becomes “x/y/z/button(1)”. But if I click on a different button first (during a subsequent recorder run) then THAT button becomes “button(1)”.

I’ve found the only safe way to proceed is to:

  1. Start a recorder run
  2. Take some action, like click a button, while I…
  3. Observe the name of the test object created
  4. Rename the test object to something useful
  5. When I save the objects, create a new folder for them
  6. Always preceded the test object name with the folder name, so the objects I use are all relative to one another

So when test object “button(1)” is created, I right-click on it, select rename, and change it to “Button(1) - Save page button”, or something like that. Delightfully, my script is updated to use the new more specific name, so my script becomes much more easy to document.

The gotchas:

  • Sometimes the act of right clicking on the button actually generates steps in the script being recorded, which leads to a lot of spurious entries to be discarded, and sometimes even spurious test objects generated

I also believe that for Windows tests, because the Xpathing is relative, and each recorder run creates a new “context” as you will, that test objects that don’t have any other unique identifying information, such as an identifying text string (most of the properties of a Windows test object are menaingless, because they change - not just every recorder run - but every single RUN of the script) that Katalon can’t find tthem again anyway.

Now, this is for Windows. My impression is that each release of the Windows driver gets better, and that web applications aren’t as heavily impacted as Windows applications.

But its also not clear to me how many of the properties collected are reliable from run to run, and how many are only valuable in the context of a single run. Extracting an item ID at the beginning of a run and reusing it only during that run might work, which is a GREAT idea I just thought of for a problem I’m trying to solve, so I’m going to go try it right now!

Thanks for your help! :slight_smile:

Jim