Search code examples
javarft

IBM-RFT: finding _really_ visible objects within a window


I have a problem with IBM-RFT (version 8.5). I am making an automated testing framework for a relatively complex application which uses a lot of tabs and panels. The problem is that I can find no way to check that the objects found by using the find method are actually visible on screen right now (are in front/on top of all the others). In other words, I need an algorythm which will only search through the currently active (brought to front) tabs.

I've done a very simple application for debugging this problem using Delphi: a single TPageControl containing 2 tabs and a single text field (TEdit) on each of the tabs. The main window is a mapped object, so this is the way I do the search:

TestObject[] objs = window().find(atDescendant(".class", "TEdit"));

If the application has just been started (only the first text field is visible - the first tab is active) then the find method will return an array of only one text field. But if I activate the second tab then (with no matter what I do after) RFT will find both text fields and there will be no way to differentiate which one of them is actually on the screen.

I've been working with several automation tools but this thing in RFT frustrates me. Here is some more information to avoid obvious advices. Believe me, I've been looking for an answer A LOT.

  1. None of the fields' properties (which are available at object inspector) change when I switch between tabs. Standard properties like .enabled and .visible are always true. And that's right since they are simply properties of the controls and not some real-time things.
  2. RFT object's method isShowing() always returns true for both fields.
  3. RFT object's method ensureObjectIsVisible() always returns true for both fields and never switches the tabs to bring the needed field into the view.
  4. I can call click(), hover() or whatever user-simulation actions for an invisible field and the test continues to run without any exceptions. Even more - if the window was not in focus at the moment of such click, it does get the focus - looks like RFT simply sends a click to the window at the center of the invisible object's rectangle.
  5. Methods getClippedScreenRectangle(), getScreenRectangle() and getVisibleArea() return the same rectangles for the invisible text field (the window was completely within the screen borders during test).
  6. When I use getImage() to produce a screenshot of the invisible text field, this results in saving the part of the window image within the screen rectangle of the invisible element.
  7. If I create mapped objects for both text fields the only difference is that in that case of a just-started-application test run will hang for several seconds during each call to invisible-and-not-yet-even-once-activated text field (for example if you call text2().exists() after the app start when the active tab is the first one, it will hang and eventually return true).
  8. This incorrect behaviour and inablility to differetiate active and inactive controls is the same on my debug Delphi-built application and the actual SUT which is written in Java.

In my debug app I can check the parent objects to understand which text field belongs to which tab but how will I find which of the tabs is activated?

QTP and TestComplete both have the functionality to make sure that the required object is actually on the screen and both will throw an exception if you try to click an object which is out of the view. Even further - QTP gives access to native properties and methods of the object which usually help when there is no any right way to do the thing (I could possibly check the ActivePageIndex in my debug app to understand which tab is active).

Did I get something wrong? Or maybe was I reading something not carefully enough? Or maybe this problem is local for my machine?

How shall I check actual object's visibility using IBM-RFT?


Solution

  • Eventually I have found a workaround. For some reason the only method which operates with what you actually can see is getChildAtPoint(). I've seen one solution using this method but it was about HTML automation and did not work in my case.

    This method finds the first direct child at the specified point. My idea is to walk through the parent objects tree from bottom to top and check that every time getChildAtPoint() returns the previous element for the previous element's screen point. When the container is actually invisible, the returned result is null. The first two methods are probably not necessary but since automation frameworks are often kind of magic-black-boxes (and RFT is definitely one of the outstanding examples), let this be something like an insurance.

    public boolean isReallySameId(GuiTestObject obj1, GuiTestObject obj2)
    {
        Object id1 = obj1.getProperty(".associatedId");
        Object id2 = obj2.getProperty(".associatedId");
        // I don't know if this property always exists
        if ((id1 == null) || (id2 == null)) return false;
        return (id1.equals(id2));
    }
    
    public boolean isReallyInScreen(GuiTestObject obj)
    {
        Rectangle screen = RootTestObject.getRootTestObject().getScreen().getScreenRectangle();
        Rectangle intersection = screen.intersection(obj.getScreenRectangle());
        return ((intersection.width > 0) && (intersection.height > 0));
    }
    
    public boolean isReallyVisible(GuiTestObject obj)
    {
        if (!isReallyInScreen(obj)) return false; // basic check
        GuiTestObject parent = (GuiTestObject) obj.getMappableParent();
        if (parent == null) return true; // top object in screen
        GuiTestObject found;
        while (parent != null)
        {
            found = (GuiTestObject) parent.getChildAtPoint(obj.getScreenPoint());
            if ((found == null) || (!isReallySameId(found, obj))) return false;
            obj = parent;
            parent = (GuiTestObject) obj.getMappableParent();
        }
        return true;
    }
    

    Ah, what a nice challenge. Thank you, RFT! :)

    P.S. I am not sure that this solution will work for all kinds of test objects. I am especially not sure whether it will or will not work for non-mappable objects. This is a solution which I needed in a particular case, so you will probably have to customize it if you've faced the same problem.