Squish Tip: Customizing QtQuick support

Squish Tip: Customizing QtQuick support

Motivation

Squish supports automating QtQuick 2.x based applications out of the box. One frequently asked question is about custom QML components or custom QtQuick controls and how Squish supports these.

Testing QtQuick with Squish is very similar to Web testing in this regard. By default, Squish will record on the basic elements that a component is made of. For custom controls this means that Squish will usually record on some BackgroundImage, Text or Rectangle item instead of the expected control.

By extending Squish and giving it knowledge of these custom types, we can record test scripts that are easier to understand and maintain.

A custom button example

Take for example the following small QtQuick application that shows two custom buttons.

First we define our custom button in a file named FrogButton.qml

import QtQuick 2.0
Item {
    id: frogButton;
    height: buttonText.height + 16
    width: buttonText.width + 24
    property alias text: buttonText.text
    signal clicked
    Rectangle {
        id: buttonRect
        anchors.fill: parent
        border.color: buttonArea.containsMouse ? "#FFFFFF" : "#49A34B"
        border.width: 2
        color: buttonArea.containsMouse ? "#5AB953" : "#49A34B"
        radius: 2
    }
    Text {
        id: buttonText
        anchors.centerIn: parent
        color: "#FFFFFF"
    }
    MouseArea {
        id: buttonArea
        anchors.fill: parent
        hoverEnabled: true
        onClicked: frogButton.clicked()
    }
}

Then we create the main QML file which shows two of these buttons with different text.

Rectangle {
    height: 200
    width: 300
    Column {
        anchors.fill: parent
        anchors.margins: 8
        spacing: 8
        FrogButton {
            text: "First"
            onClicked: txt.text = "Leap"
        }
        FrogButton {
            text: "Second"
            onClicked: txt.text = "Jump"
        }
        Text {
            id: txt
            text: "Quack"
        }
    }
}

The example can be started by passing the main QML file to the qml application which ships with Qt 5.

Testing the example in Squish

Picking on the example application with Squish will either yield the Rectangle or the Text item but never the parent FrogButton item. This is because both items fill the complete visual area of their parent FrogButton item.

Picking a custom button

Additionally, the object name for the Rectangle item inside those buttons is rather generic. Rectangles simply don’t have any good properties to identify them uniquely. The name will contain an occurrence value as soon as more than one FrogButton is being displayed at the same time. For the example application, the Rectangle in the second button will have a name of {container=':_QQuickWindow' id='buttonRect' occurrence='2' type='Rectangle' unnamed='1' visible='true'}.

However there’s a solution. Almost all behavior of QtQuick supported in Squish can be configured via Squish QML Extensions. These extensions are essentially normal QML files, and they allow customization of picking and recording behavior, as well as name generation in Squish.

Writing a Squish QML Extension

To write a Squish QML Extension, we will start with a QML file that creates a single SquishHook object, and which contains an isIgnored() function.

import QtQuick 2.1
import com.froglogic.squish.qtquick 0.1
SquishHook {
    priority: 120
    function isIgnored( item ) {
        if ( qmlType( item ) == 'FrogButton' ) {
            return false;
        }
        if ( qmlType( item.parent ) == 'FrogButton' ) {
            return true;
        }
        return unhandled;
    }
}

The priority: value here was chosen to be higher than the standard extensions that ship with Squish. This means that this extension will always be asked first about what to do with a QtQuick item.

The first check inside the isIgnored() function tests if the current item is a FrogButton. The check is needed to override the default behavior of filtering out objects if they are of type Item. The default rule comes from the fact that such an item does not have any kind of visual representation (it’s essentially invisible) and thus Squish ignores it.

The second check tests the type of the immediate parent item. In our example, this means that if isIgnored() is called for either the Rectangle or Text items inside the FrogButton then they will be ignored.

The final return at the end instructs Squish to ask another extension for the item in question since this extension should only customize the behavior for a FrogButton and its children.

Both checks together provide the desired behavior of always picking and recording on FrogButton instead of any of its child items.

After putting the above extension into lib/extensions/qtquick/ inside a Squish installation, picking will look slightly different:

Picking with Extension

Customizing object names in Squish QML Extensions

Now that picking works there’s still one task left, the object name for the button is still not quite perfect: {container=':_QQuickWindow' type='FrogButton' unnamed='1' visible='true'}

As soon as two FrogButtons are present in the same container they will get identical names, which will then result in an occurrence= value added to the object name. One way to improve this is to add the button’s text to the object name, assuming that buttons do not change their text after creation. This can be achieved from within a Squish QML Extension by adding another function to the extension file:

function extraPropertiesFor( item ) {
    if ( qmlType( item ) == 'FrogButton' ) {
        return [ 'text' ]; 
    }
    return unhandled;
}

This is very similar to the checks done in isIgnored(). In this case the function has to return a list of Qt property names that should be included in the object name generated by Squish. For FrogButton it will add the text property only.

After adding this function to the extension, the new object name when picking the button will change to {container=':_QQuickWindow' text='First' type='FrogButton' unnamed='1' visible='true'}. Both buttons in the example will now have unique object names.

Wrapping it up

QtQuick is a very customizable UI technology and as such Squish also allows customizing how objects and names are handled when recording on a QtQuick based application. The shown example is of course very simple, but much more complex controls can be supported in the same way.

The complete example code as well as the Squish QML Extension and an example Squish testsuite is available for download.

3 Comments

  1. Jeremiah 2 months ago

    I tried this feature and it didn’t work for me.

    My QML object is SearchPage and the hierarchy is Page -> GFAPageBase -> GFAPage -> SearchPage. Squish can only detect GFAPageBase out of all 4.

    Is there a way I can detect “SearchPage” instead? Or is there something I’m doing wrong?

    • Author
      Stefan Gehn 2 months ago

      When writing custom Squish QML Extensions it usually helps to use the QML console.log() to see when and for which item the extension is called. For example one could simple put the following at the start of the isIgnored function:
      console.log(“isIgnored: ” + item);
      That way one can see if the item in question ever reaches the extension.

      From the explanation, the hierarchy rather sounds like a type-inheritance hierarchy than a hierarchy of QtQuick Items. In case of unknown/missing types and for more complex cases than the simple button example it’s probably better to ask this via our customer support. Writing Squish QML Extensions always needs a bit of knowledge about the application and its qml code and pasting (even parts of) the code is probably not a good idea.

      • Jeremiah 2 months ago

        I got it to work! Thanks for your reply.

        Unfortunately, it only works if my object has a custom property. I had to add a custom property that my page didn’t need to get it to work. Is there a way to make it work without adding this custom property?

        Also, I am unable to see my log in under Runner/Server Log, however, I do see my extension being loaded. Is there a place in the Squish GUI I can see this?

Leave a reply

Your email address will not be published. Required fields are marked *

*