Rapise can do UI test automation for Java applications on Windows. It can automate standard AWT and SWING controls. To talk to Java runtime it uses Java Access Bridge. Using it Rapise can execute Java statements within a process of the application under test and get results.

One day we were contacted by a customer who missed support for JPopupMenu control. We managed to add support for it within a very short time. Most of the time was spend to analyze Java API and see what is so different between  JMenu, JList and JPopupMenu. We already had support for JMenu and JList and thought we can just reuse it.

We found that to enumerate items in JMenu one should use getMenuComponentCount() and getMenuComponent(index).

for(int i = 0; i < menu.getMenuComponentCount(); i++)
{
    System.out.println(menu.getMenuComponent(i).getName());
}

And for enumeration of JList items - getModel().getSize() and getModel().getElementAt(index).

for(int i = 0; i < list.getModel().getSize(); i++)
{
    System.out.println(list.getModel().getElementAt(i));
}

None of those methods were found in JPopupMenu so we had to stick to getComponentCount() and getComponent(index).


for(int i = 0; i < popup.getComponentCount(); i++)
{
    System.out.println(popup.getComponent(i).getName());
}

For simple popups (without submenus) we implemented list-like behavior pattern with SelectItem method and a few getters ItemCount, ItemIndexByName and ItemNameByIndex.

Here is the rule and behavior attached to it. Rapise uses JavaScript.

 

Rule

Rules are used to identify type of UI control and which behaviors (properties, methods) can be applied to it.

The rule inherits properties from generic Java object, has List flavor (to display fancy icon in the object tree) and matches controls with javax.swing.JPopupMenu class.

/** @rule */
var SeSJavaSwingPopupMenuRule = new SeSMatcherRule(
    {
        object_type: "JavaSwingPopupMenu",
        object_flavor: "List",
        extend_rule: "JavaObject",
        
        componentType: "javax.swing.JPopupMenu",
        
         /** @behaviors */
        behavior: [JavaObjectPopupSelectableBehavior]
    }
);

 

Behavior

Notice those SeSJavaInvokeExpr calls that pass Java statements to the application under test.

/** @behavior */
var JavaObjectPopupSelectableBehavior =
{
    actions: [
        {
            actionName: "SelectItem",
            /** @action */
            DoAction: function(item)
            {
                var size = SeSJavaInvokeExpr(cp, 'getComponentCount()', true);
                for (var i = 0; i < size; i++)
                {
                    if (SeSJavaInvokeExpr(this.instance, 'getComponent(%0).getName()', true, [i]) == item)
                    {
                        var child = SeSGetJavaChildAt(cp, i);
                        var rc = SeSJavaObjectScreenRectangle(child);
                        var x = rc.x + (rc.w >> 1);
                        var y = rc.y + (rc.h >> 1);
                        Global.DoMouseMove(x, y);
                        g_util.LButtonDown();
                        Global.DoSleep(25);
                        g_util.LButtonUp();
                        return true;
                    }
                }
                return false;
            }
        }       
    ],
    properties:
    {    
        /** @property */    
        ItemCount:
        {
            Get: function()
            {
                return SeSJavaInvokeExpr(this.instance, 'getComponentCount()', true);
            }
        },
        /** @property */
        ItemIndexByName:
        {
            Get: function(name)
            {
                var size = SeSJavaInvokeExpr(this.instance, 'getComponentCount()', true);
                for (var i = 0; i < size; i++)
                {
                    if (SeSJavaInvokeExpr(this.instance, 'getComponent(%0).getName()', true, [i]) == name)
                    {
                        return i;
                    }
                }
                return -1;    
            }
        },
        /** @property */
        ItemNameByIndex:
        {
            Get: function(index)
            {
                return  SeSJavaInvokeExpr(this.instance, 'getComponent(%0).getName()', true, [index]);                
            }
        }
    }
}

 

Usage

And finally example of how to use new functionality.

var popup = SeS('PopupMenu');

var count = popup.GetItemCount();
Tester.Message(count);
for(var i = 0; i < count; i++)
{
    var name =     popup.GetItemNameByIndex(i);
    Tester.Message(name);
}

var index = popup.GetItemIndexByName("Menu1Item3Checkbox");
Tester.Message("Menu1Item3Checkbox index:" +index);

popup.DoSelectItem("Menu1Item3Checkbox");

 

Conclusion

Rapise offers a lot for those who want to extend and customize it. Adding support for a new UI widget is a relatively simple task that can be completed within a few hours.