As AlexKaras said, apps developper are more and more miseducated about maintenability and they rarely do it the right thing (add an unique id property with understandable value which doesn't change on time/techno but which is related only on a functional thing). Rather that, id are autogenerated ones and we can't rely on.
So how to manage that ? The Alex's way is a good one but if you dare you can even go farther and use only find/findChild approach. I use this method since a long time and no application resist to me 😉
The important thing is to explain what is the cost of missing correct id (test, documentation, refactoring, reversibility, onboarding, ..) thus driving the managers to force apps developpers to add it (money has always the last word).
The scripting approach is oftenly seen as a costly and less sustainable. I don't think so if you do good scripting job. Simply don't make the same errors than apps developpers 😁
A sample of a find approach based on smart developpers who added unique id:
(qa.XXX methods are from my framework, qa.system.findIt is based upon findChild and qa.web.findIt is based upon XPath). This extract is to manage an insurance subscription,here the Customer Activity screen.
saisieActivite:function(Activite) {
let trace = this.methodStart("saisieActivite(" + Activite + " ) - Saisie du formulaire Activité");
try {
let container = qa.system.findIt(qa.system.currentBrowser.currentPage, "ObjectIdentifier", 'contactInformationform', qa.system.time.longer, 15, true, true);
let recherche = Activite.split(";");
let fields;
let dummy;
for (let i = 0; i < recherche.length; i++) {
if (recherche[i] != "") {
fields = recherche[i].split("=");
switch (fields[0]) {
case "surname" :
case "contractorFirstName" :
case "birthDate" :
case "homeNumber" :
case "zipCode" :
dummy = qa.system.findIt(container, "ObjectIdentifier", fields[0], 0, 10, true, true);
this.input(dummy, fields[1], "1");
break;
case "streetName" :
dummy = qa.system.findIt(container, ["ObjectIdentifier", "ObjectType"], ['streetName','TextBox'], qa.system.time.second, 10);
this.input(dummy, fields[1]);
break;
case "city" :
// Sélection de ville sur CP, attendre le remplissage de la liste des villes
aqUtils.Delay(qa.system.time.seconds);
// Dans le cas où on a plusieurs villes correspondantes et que la sélection n'est pas automatique
let select = qa.web.findIt('name', "city", "SELECT");
if (aqString.Compare(select[0].wText,"--Choisissez",true)==0){
qa.web.selectValueInHTMLCombo(fields[0], select[0].wItem(1));
}
break;
case "activity" :
case "steed" :
case "title" :
case "amddressType" :
qa.web.selectValueInHTMLCombo(fields[0], fields[1]);
break;
default :
throw Error("Saisie activité, champ '" + fields[0] + "' non géré/incorrect !");
}
}
}
this.etapeSuivante();
}
catch(e) {
errorString = qa.system.logExceptionByLevel(e, trace);
}
finally {
return this.methodStop(true);
}
},
And thus, testing this peculiar page is a call of saisieActivite(TestData), like saisieActivite("title=M.;surname=Bob;birthDate=30/12/1971;zipCode=75000;city=Paris")