Forum Discussion

jmarkman's avatar
jmarkman
Occasional Contributor
6 years ago

JScript, compile time, and FindChild: Using objects to organize namemappings, doin' it wrong?

I asked a question recently about the usage of regex & wildcards in the FindChild method, and this is where that question came from. My workplace has a test project that hasn't been maintained and was quite a mess, so I went in and updated it, hoping to make the test not only more robust but more legible. My master plan was to create four classes that would represent the four different chunks of the UI that would be tested. Of course, JScript doesn't have "classes" like those who have programmed in langauges like C#, Python, Java, etc. are used to, but one can do something like

 

function UIElement()
{
// Properties this.Property1 = "hello"; this.Property2 = 3; }

var element = UIElement();

 

Now this seemed great at first glance, because I could represent some GUI element like an element with a grid inside of it like this

 

var parentElement = Aliases.Program.InformationDisplay.InfoDisplayWindow;

function InformationGrid()
{
    this.Grid = parentElement.FindChild("className", "info_container", 3);
    this.GridRows = this.Grid.FindAllChildren("className", "info-details", 5);

    this.GetDogName = function(rowIndex)
    {
        var name = this.GridRows[rowIndex].FindChild("className", "name-panel", 3).contentText;
        Log.Message(name);
    }
}

which would reduce method size despite introducing more methods, introduce code clarity, and provide some level of abstraction for future usage; all pending code review. I wrote the four JScript classes I wanted in another script unit within the project. I had hoped I could go through the following chain of steps for the tests in question:

  1. Ensure that our software is running - if it isn't, open it (we have a method for this)
  2. Open the window in our software that we want to test (also have a method for this)
  3. Call an initializer method from the script unit I just wrote which uses a mix of aliases and FindChild to point out where the UI elements are in the object hierarchy
  4. Run the test, which would then call the various classes and perform the tests
  5. Finish the test and close the window

However, when I run the test, I get a JScript runtime error that the very first property of the very first class in the script unit that I've written to house my GUI representing classes is "null or not an object"

function WorkSearchBar()
{
  this.SearchTypeDropdown = schedDisplaySearchBar.FindChild("className", "dropdown", 6); // Errors out here
  this.DropdownButton = this.SearchTypeDropdown.FindChild("className", "btn btn-default dropdown-toggle ng-binding", 2);
  this.SubmitButton = schedDisplaySearchBar.FindChild(new Array("className", "idStr"), new Array("btn btn-default", "submitSearch"), 8);

  this.SetDropdown = function(item)
  {
    this.DropdownButton.DropDown();
    this.SearchTypeDropdown.FindChild("contentText", item, 4).Click();  
  }
}

The error makes sense because the software isn't open yet (would more than likely be a null reference exception in C#/Java/etc), but this script unit shouldn't be hit yet. In the workflow above, I have the software set to open before anything else in the test method. Shouldn't this only come into play when its needed because of JIT compilation? Is JScript not JIT compiled? Is this "representing GUI elements as objects using a mix of aliases and FindChild" plan an unsupported approach? 

8 Replies

  • tristaanogre's avatar
    tristaanogre
    Esteemed Contributor

    The problem is that the parentElement, being outside a function, is assigned immediately upon starting up the test run.  So, if the application isn't running yet, that will have a "null" value or, if not null, at least be a "stub" object with no properties but the Exists property set to False.

    You should probably wrap that variable declaration in such a way that it is not called until it is needed to avoid the above.

    • tristaanogre's avatar
      tristaanogre
      Esteemed Contributor

      Something more like this:

      function parentElement() {
      
          return Aliases.Program.InformationDisplay.WaitAliasChild('InfoDisplayWindow', 1000);
      }
      function InformationGrid()
      {
          this.localParent = parentElement();
      this.Grid = this.localParent.FindChild("className", "info_container", 3); this.GridRows = this.Grid.FindAllChildren("className", "info-details", 5); this.GetDogName = function(rowIndex) { var name = this.GridRows[rowIndex].FindChild("className", "name-panel", 3).contentText; Log.Message(name); } }

       

       

      • jmarkman's avatar
        jmarkman
        Occasional Contributor

        The problem is that the parentElement, being outside a function, is assigned immediately upon starting up the test run.  So, if the application isn't running yet, that will have a "null" value or, if not null, at least be a "stub" object with no properties but the Exists property set to False.

         

        Ah, this is good to know. I'll use the wrapper you've suggested and see what that does for me, but I get the feeling this is what I needed.

  • AlexKaras's avatar
    AlexKaras
    Champion Level 3

    Hi,

     

    One precaution based on real experience before you went too far this way:

    a) TestComplete does not provide any intellisense or navigational means that display those internal functions;

    b) Internal functions cannot be 'included' from another file and all their body must be provided inline.

     

    The result of the two points from above will be that you will get a long file with no hints as for what internal functions it contains and the means to quickly navigate to them.

    So you will have to remember the internal contents of the top-level functions (or manage this content some other way) and code debugging will be less convenient as well.

     

    In the inherited progect that I mentioned as real experience we refused from internal functions as it was too inconvenient.

     

    • cunderw's avatar
      cunderw
      Community Hero

      There are a few feature requests to allow the intellisense to include these internal methods and better support classes when using JavaScript, the day that is implemented will make me sooooo happy. 

    • jmarkman's avatar
      jmarkman
      Occasional Contributor

      a) TestComplete does not provide any intellisense or navigational means that display those internal functions;

       

       

      I noticed this, it's rather inconvenient even if I have that file commented thoroughly, because even with comments someone else would have to dive into the file instead of just being able to get some intellisense on it.

       

      b) Internal functions cannot be 'included' from another file and all their body must be provided inline.

       

      I don't understand; is this in regards to having to copy over this file if a related project is created? I thought this is what USEUNIT allowed for.

       

      Regardless, I'll keep these points in mind.

      • AlexKaras's avatar
        AlexKaras
        Champion Level 3

        Hi,

         

        > I don't understand; [...]

        No. I meant that the whole body of the internal function must be provided inline.

        I.e. you must write

        function InformationGrid()
        {
            this.Grid = parentElement.FindChild("className", "info_container", 3);
        
            this.GetDogName = function(rowIndex)
            {
                Log.Message(name);
            }
        }

        but cannot write like this (pseudocode):

        function InformationGrid()
        {
            this.Grid = parentElement.FindChild("className", "info_container", 3);
        
            this.GetDogName = function(rowIndex)
            {
                //USEUNIT GetDogName.js; // <== a kind of include
            }
        }

         

        For the non-trivial internal functions, combined with absent navigation/intellisense for internal functions (and absense of something like header files in C\C++ (.h files)), the result of this will be that you will have to continuously scroll up and down through the file in order to locate the function that you need.

         

        Yes, in runtime everything works as expected, but it was exactly because of debug/navigation inconveniences we stopped using internal functions in our project.