Layout validation for text truncation in Java applications

Last edited on

Introduction

Application layout validation is a very difficult task no matter the test automation tool. Some level of layout validation can be achieved even using tools which verify object properties and don't rely on screenshot comparison. For Java applications it can be done by analyzing the return value of the getPreferredSize() method of the Component and JComponent classes. This value holds the size the component prefers do be rendered in.

Solution

This article presents a JavaScript solution. Similar solutions can be implemented in other scripting languages supported by Squish. We'll start by implementing a function, which returns true if the current size of the given component isn't less than the return value of getPreferredSize().

function isSufficientlySized(obj) {
    var size = obj.getSize();
    var preferred = obj.getPreferredSize();
    return size.width >= preferred.width && size.height >= preferred.height;
}

Next, we'll define a wrapper for above function, which calls the Squish API functions test.pass or test.fail depending on the isSufficientlySized result. This allows us to check the Squish Results for which components were analyzed and the outcome of the analysis.

function verifySize(obj) {
    var name = objectMap.symbolicName(obj);
    if ( isSufficientlySized(obj) ) {
        test.pass("Sufficient size of " + name);
        return true;
    } else {
        test.fail("Insufficient size of " + name);
        return false;
    }
}

Next we'll create a function which generates a list of all components contained in the given frame or window. Function fetchAllComponents(obj) recursively traverses all components whose obj is an ancestor.

function fetchAllComponents(obj)
{
    var children = object.children(obj);
    var components = [obj];

    for (var i in children) {
        var child = children[i];
        components = components.concat(fetchAllComponents(child));
    }
    return components;
}

Finally, we'll implement the verifyDialogLayout(dlg, output) function, which is called directly from the test case. It takes two arguments: main component object reference (frame,window) and image filename, where the screenshot of the main component is stored in the event the case validation for correct layout is not a positive value. All components whose size is not sufficient will be drawn with a red border on the captured screenshot. This is done by creating an overlay of suitably shaped JDialog instances.

function verifyDialogLayout(dlg, output) {

    var components = fetchAllComponents(dlg);

    dlg.getClass().getClassLoader().loadClass("javax.swing.SwingUtilities");
    if ((new java_awt_Dialog(object.createNull(java_awt_Dialog))).getClass().isAssignableFrom(dlg.getClass())) {
      dlg.setModalityType(java_awt_Dialog$ModalityType.MODELESS);
    }

    var ok = true;
    for (var i in components) {
        var w = components[i];

        if (!verifySize(w)) {
            ok = false;

            var location = new java_awt_Point();
            javax_swing_SwingUtilities.convertPointToScreen(location, w);

            var border = 2;
            var size = w.getSize();
            drawRect(location.x + size.width, location.y, border, size.height + border);
            drawRect(location.x - border, location.y + size.height, size.width + border, border);
            drawRect(location.x - border, location.y - border, border, size.height + border);
            drawRect(location.x, location.y - border, size.width + border, border);
        }
    }

    if (!ok) {
        var pix = grabWidget(dlg);

        // Save image on the computer where the application is running.
        // (Use object.grabScreenshot() to have the image on the
        // computer where the Squish IDE (or squishrunner) is being
        // executed. See https://doc.qt.io/squish/squish-api.html#object-grabscreenshot-function)
        pix.save(output, "PNG");

        test.log("Wrote illustration to " + output);
    }
}

function drawRect(x, y, width, height) {
    var bar = new javax_swing_JDialog();
    var panel = new javax_swing_JPanel();
    panel.setBackground(java_awt_Color.RED);
    bar.add(panel);

    bar.setBounds(x, y, width, height);
    bar.setUndecorated(true);
    bar.setVisible(true);
    bar.toFront();
}

Example

To present this solution in action we use the Java Swing application AddressBook. First, we need to make some components smaller than they should be. To do that we implement function messWithSizes(), which sets incorrect size for some components.

import * as names from 'names.js';
function messWithSizes()
{
    var foreNameEdit = waitForObject(names.addressBook_AddForename_JTextField);
    foreNameEdit.setSize(foreNameEdit.getWidth(), 8);
    var cancelButton = waitForObject(names.addressBook_AddCancel_JButton);
    cancelButton.setSize(20, 20);
    var foreNameLabel = waitForObject(names.addressBook_AddForename_JLabel);
    foreNameLabel.setSize(50, foreNameLabel.getHeight());
}

function main() {
    startApplication("AddressBook.class");

    activateItem(waitForObjectItem(names.addressBook_JMenuBar, "File"));
    activateItem(waitForObjectItem(names.fileJMenu, "New..."));
    activateItem(waitForObjectItem(names.addressBook_JMenuBar, "Edit"));
    activateItem(waitForObjectItem(names.editJMenu, "Add..."));
    type(waitForObject(names.addressBook_AddForename_JTextField), "MyName");

    messWithSizes();
    verifyDialogLayout(waitForObject(names.addressBook_AddDialog), "layout_checkSwing.png");
}
Squish IDE: Test Results View

Additionally, the screenshot was generated with failed components marked as follows: