Forum Discussion

hhagay's avatar
hhagay
Contributor
9 years ago

identify dynamic objects properties

Hello

The AUT uses react to generate ids upon page load.

 

Consider the following text input objects.

 

<input id="ssn_mask" class="field-input field-input--ssn" name="ssn_mask" value="" data-reactid=".0.2.0.0.2.0.4.5.1.0.0.2.0" type="text"/>
<input id="ssn_mask" class="field-input field-input--ssn" name="ssn_mask" data-reactid=".0.2.0.0.2.0.4.5.1.0.0.2.1" type="text"/>
<input id="ssn_mask" class="field-input field-input--ssn" name="ssn_mask" data-reactid=".0.2.0.0.2.0.4.5.1.0.0.2.2" type="text"/>

What is the best way to get the objects properties (reactid I assume?) and SetText to each?

 

Thank you

 

  • bbi's avatar
    bbi
    Contributor

     

    * i don't know exactly what you want but i've made the following method to search by Xpath query inside web page.

     

     

     

      /**
       * <a id="web.findItWeb"></a>
       * Rechercher des objets via requête XPath sur une page web<br>
       * <br>
       * <font color=Red><i><b>Limitations<br>
       * - limitation technique, TestComplete ne gère que les requêtes XPath 1.0</b></i></font>
       * @function
       * @param {string} Property - Nom de la propriété à rechercher
       * @param {variant} Value - Valeur de la propriété à rechercher
       * @param {string} [Type=*] - Type de l'objet (<i>SELECT</i>, <i>INPUT</i>, <i>DIV</i>, etc..) pour réduire le champ de recherche
       * @param {object} [Page=current page] - Page/élément web à rechercher dedans
       * @param {number} [Timeout=0] - Si <b>&gt;0</b> alors répéter la recherche pendant <i>Timeout</i> millisecondes
       * @param {boolean} [Refresh=false] - <b>true</b> alors rafraichir la page avant de faire la recherche
       * @param {boolean} [Parent=false] - <b>true</b> alors renvoyer l'objet parent de celui recherché
       * @param {boolean} [Partial=false] - <b>true</b> alors recherche sur <i>Property contient Value</i> et non pas <i>Property = Value</i>
       * @returns {object} Renvoie un <b>objet web TestComplete</b> si il a été trouvé ou <b>null</b> s'il n'a pas été trouvé
       */
      qa.web.findItWeb = function (Property, Value, Type, MyPage, Timeout, Refresh, Parent, Partial) {
        if ((Property == undefined) || (Value == undefined)) {
          if (qa.system.findDebug) {
            Log.Warning('Appel de qa.web.findItWeb() avec un paramètre obligatoire non renseigné (Property ou Value)');
          }
          return null;
        }
        MyPage  = qa.system.checkUndefined(MyPage, Sys.Browser().Page('*'));
        Type    = qa.system.checkUndefined(Type, "*");
        Refresh = qa.system.checkUndefined(Refresh);
        Parent  = qa.system.checkUndefined(Parent);
        Partial = qa.system.checkUndefined(Partial);
        Timeout = qa.system.checkUndefined(Timeout, 0);
        var toFind;
        if (qa.system.findDebug) {
          var TimeoutStr = (Timeout == 0 ? 'no time out' : Timeout.toString());
          Log.Message('qa.web.findItWeb(' + Property.toString() + ', ' + Value.toString() + ', ' + Type.toString() + ', [MyPage], ' + TimeoutStr + ', ' + Refresh.toString() + ', ' + Parent + ', ' + Partial + ')');
        }
        if (Partial) {
          toFind  = "//" + Type + "[contains(@" + Property + ', "' + Value + '")]';
        }
        else {
          toFind  = "//" + Type + "[@" + Property + ' ="' + Value + '"]';
        }
        if (Parent) {
          toFind = toFind + '/..';
        }
        var objectfind;
        try {
          if (Timeout == 0) {
            if (Refresh) {
              MyPage.Refresh();
            }
            objectfind = MyPage.EvaluateXPath(toFind);
            if (objectfind != null) {
              objectfind = (new VBArray(objectfind)).toArray();
            }
          }
          else {
            var finding = false;
            var chrono  = HISUtils.StopWatch;
            chrono.Start();
            while ((!finding) && (chrono.Split() < Timeout)) {
              if (Refresh) {
                MyPage.Refresh();
              }
              objectfind = MyPage.EvaluateXPath(toFind);
              if (objectfind != null) {
                objectfind = (new VBArray(objectfind)).toArray();
                finding = objectfind.Exists;
              }
            }
          }
        }
        catch(e) {
          objectfind = null;
        }
        finally {
          if (qa.system.findDebug) {
            if ((objectfind == undefined) || (objectfind == null)) {
              Log.Message('qa.web.findItWeb() renvoie un objet null ou undefined');
            }
            else {
              Log.Message('qa.web.findItWeb() a trouvé un objet');
            }
          }
        return objectfind;
        }
      };

     

    Adapt it to your code (here it's a method part of my framework namespace qa.web so method signature is quite unusual)

     

     

    For example to search a DIV item by it's html class (webobject could be the Sys.Browser('*').Page('*') or an object of a page):

    item = qa.web.findItWeb('class', 'name_of_the_class_to_look_for', 'DIV', webobject;

     

    Or searching a SELECT item by it's html name:

    var Page = Sys.Browser().Page(_SHRoute);
    var TypeRoute = qa.web.findItWeb("name", "name_to_look_for", "SELECT", Page);
    TypeRoute[0].ClickItem(_SHRessources[pRouteType]);

     

    Or searching an INPUT item ...

    var FileRoute = qa.web.findItWeb("name", "name_to_look_for", "INPUT", Page);

     

    Or searching an item only by it's id on the current page ...

    var ItemToFind = qa.web.findItWeb("id", "id_to_look_for");

     

     

     

    * To set text to an input i've made the following method, i let you dig inside a little and try it and if you still need help tell me.

     

      /**
       * <a id="web.setTextInput"></a>
       * Saisir un texte dans un champ de saisie d'un objet web
       * @function
       * @param {Object} TextInput - Objet web contenant une propriété texte acceptant la saisie
       * @param {string} Value - Texte à saisir
       * @param {boolean} [Validate=false] - <b>true</b> alors appuyer sur <i>ENTREE</i> après saisie
       * @param {string} [PropertyName=caption] - Nom de la propriété de <i>TextInput</i> à utiliser
       * @param {boolean} [Force=false] - <b>true</b> alors saisir et ignorer le contrôle de la valeur saisie
       * @param {boolean} [Autocomplete=false] - <b>true</b> alors vérifier à chaque saisie de caractère si par autocomplete la valeur du champ est bien renseignée (incompatible avec le paramètre <i>Force</i>)
       * @returns {boolean} <b>true</b> si l'opération s'est bien déroulée et, si <i>Force</i> est à <b>false</b>, que la valeur demandée est bien saisie
       */
      qa.web.setTextInput = function (TextInput, Value, Validate, PropertyName, Force, Autocomplete) {
        if ((TextInput == undefined) || (TextInput == null)) {
          return false;
        }
        Validate     = qa.system.checkUndefined(Validate);
        PropertyName = qa.system.checkUndefined(PropertyName, 'caption');
        Force        = qa.system.checkUndefined(Force);
        Autocomplete =  qa.system.checkUndefined(Autocomplete);
        var resultat = true;
        try {
          if (Force) {
            TextInput.Click();
            TextInput.Keys('^a');
            TextInput.Keys('[Del]');
            TextInput.Keys(Value);
            if (Validate) {
              TextInput.Keys('[Enter]');
            }
          }
          else {
            var i = 0;
            if (Autocomplete) {
              var j;
              while ((!(TextInput.WaitProperty(PropertyName, Value, qa.system.time.micro))) && (i<10)) {
                TextInput.Click();
                TextInput.Keys('^a');
                TextInput.Keys('[Del]');
                var tableau=Value.split("");
                for (var j=0; j<tableau.length; j++) {
                  TextInput.Click();
                  TextInput.Keys(tableau[j]);
                  aqUtils.Delay(qa.system.time.smaller, 'Autocomplete text input');
                  if (TextInput.WaitProperty(PropertyName, Value, qa.system.time.micro)) {
                    break;
                  }
                }
                i++;
              }
            }
            else {
              while ((!(TextInput.WaitProperty(PropertyName, Value, qa.system.time.micro))) && (i<10)) {
                TextInput.Click();
                TextInput.Keys('^a');
                TextInput.Keys('[Del]');
                TextInput.Keys(Value);
                i++;
              }
            }
            if (TextInput.WaitProperty(PropertyName, Value, 0)) {
              if (Validate) {
                TextInput.Keys('[Enter]');
              }
            }
            else {
              resultat = false;
            }
          }
        }
        catch(e) {
          resultat = false;
        }
        return resultat;
      };

     

    • hhagay's avatar
      hhagay
      Contributor

      Good morning - Bonjour bbl

       

      Thank you very much, I will go through your code, implement it and update you.

       

      Merci beaucoup

       

       

  • baxatob's avatar
    baxatob
    Community Hero

    Hi,

     

    Can you show how your text fields are presented 1) on the web page and 2) in the Object Browser?

    • hhagay's avatar
      hhagay
      Contributor

      Good morning

       

      I have attached a word document comprises of the fields you requested.

      Let me know if you need additional information.

       

       

      Thanks

       

       

      • tristaanogre's avatar
        tristaanogre
        Esteemed Contributor

        What happens if you do a recording of entering data in the SSN fields? How does TestComplete map them? TestComplete is pretty "smart" when it comes to such stuff so, rather than attempting to code around it, try a record and see what it comes up with.

         

        As it is, there is a class with class name "field field--grouped" and there is a label asociated with it for "ssn_mask". That MIGHT be able to be mapped using those properties.  Then, you have three child objects underneath ordered from left to right by reactid.  That MIGHT be how you need to access those fields... start with the parent ssn_mask object and then map the three objects underneath, perhaps using wildcards for the reactID's like *.0, *.1, and *.2 to distinguish them.