Squish Tip: Verification of Text Colors

Squish Tip: Verification of Text Colors

 

Motivation

A common thing tests like to verify is whether text has a certain color. If you wish to avoid screenshot comparisons, then the way to do this differs depending on the toolkit and the API you are using to present text to the user. This article will explain some of the different ways text can be presented, and how to verify their color in a test case.

Squish/Web

There is a standard way to color text in web pages – via CSS – Squish Docs

Squish/Web exposes a style() method to each DOM node that lets you find out what the CSS style properties are for it. The returned object has only 1 method, value(), which takes as a string, the name of the CSS property you want to get.

In Squish 6.2, it is required to manually write the verification point, although in a future version of Squish, the style properties will be exposed to the Properties window in the IDE, and then it will be possible to insert verification points from there.

Squish/Java

In Java, text can be colored in many different ways. Sometimes the color is determined by style, and other times it depends on the HTML text that is being rendered, and other times, it is a function or a simple property. In general, some knowledge of how the program was written is needed.

Also, applications can be written in Swing, SWT, or JavaFX which all use a different API.

Swing

JLabel

The text in a JLabel can be colored by HTML text, containing a <font color=> tag. Alternately, we can call JLabel.setForeground().


jlbLabel1 = new JLabel("Red Foreground Color");
jlbLabel1.setForeground(Color.red);
jlbLabel2 = new JLabel("<html>Text color: <font color='red'>red</font></html>");

Examining an AUT with labels colored in these different ways, we must have an understanding of how the color was set in order to know what to verify. The foreground property of JLabel is quite easy to verify – the property shows up in the properties window, so we can use the object picker and insert scriptified verification points.

In the case where the text is encoded as HTML, we need to examine the text property itself, we must do regex scanning, or proper HTML parsing of the string to determine the actual color. (Python’s HTMLParser might help here).

    test.compare(str(waitForObjectExists(<span class="code-quote">":jLabel Usage Demo.Red Foreground Color_JLabel"</span>).foreground.colorspace), <span class="code-quote">"java.awt.color.ICC_ColorSpace@32ebf561"</span>)
    test.compare(waitForObjectExists(<span class="code-quote">":jLabel Usage Demo.Red Foreground Color_JLabel"</span>).foreground[<span class="code-quote">"class"</span>], <span class="code-quote">"java.awt.Color"</span>)
    test.compare(str(waitForObjectExists(<span class="code-quote">":jLabel Usage Demo.Red Foreground Color_JLabel"</span>).foreground), <span class="code-quote">"java.awt.Color[r=255,g=0,b=0]"</span>)
    test.compare(waitForObjectExists(<span class="code-quote">":jLabel Usage Demo.Red Foreground Color_JLabel"</span>).foreground.alpha, 255)
    test.compare(waitForObjectExists(<span class="code-quote">":jLabel Usage Demo.<html>Text color: <font color='red'>red</font></html>_JLabel"</span>).text, <span class="code-quote">"<html>Text color: <font color='red'>red</font></html>"</span>)

 

JTree and JTable cells

With the trees and tables, each colored cell is rendered with a Component – this is similar to a Qt Delegate, and it is returned at render-time. From a test script, we can call the JTable.getCellRenderer() function to get the renderer, and from that, getTableCellRendererComponent() to get the Component, and then use Java to call Component.getForeground() or .getBackground() to get its color. It is rather complex, so here are some helper functions for you.

def main():
    startApplication(<span class="code-quote">"my_aut"</span>)

    #...
    jtable = waitForObject(<span class="code-quote">"{type='javax.swing.JTable'}"</span>)

    # This is an instance of javax.swing.plaf.ColorUIResource
    # http:<span class="code-comment">//docs.oracle.com/javase/8/docs/api/javax/swing/plaf/ColorUIResource.html
</span>    foreground = get_jtable_cell_foreground(jtable, 0, 0)

    test.compare(255, foreground.getRed())

def get_jtable_cell_foreground(jtable, row, column):
    component = get_jtable_cell_component(jtable, row, column)
    <span class="code-keyword">return</span> component.getForeground()

def get_jtable_cell_background(jtable, row, column):
    component = get_jtable_cell_component(jtable, row, column)
    <span class="code-keyword">return</span> component.getBackground()

def get_jtable_cell_component(jtable, row, column):
    renderer = jtable.getCellRenderer(row, column);
    value = jtable.getModel().getValueAt(row, column)
    selectedColor = jtable.getSelectionModel().isSelectedIndex(row)
    hasFocus = True
    component = renderer.getTableCellRendererComponent(jtable, value, selectedColor, hasFocus, row, column)
    <span class="code-keyword">return</span> component

JavaFX

Labeled controls

JavaFX controls such as Button, that contain text, derive from Labeled and have a property textFill which can be accessed from the SquishIDE for the purpose of making verification points.

Table Cells

In JavaFX, TableRow and TableColumn objects are also Labeled, so they too have a textFill property. Inserting a scriptified verification point shows you that many of its sub-properties are verified. You can easily eyeball them and remove the redundant ones.

    mouseClick(waitForObjectItem(<span class="code-quote">":Address Book - MyAddresses.adr.itemTbl_table-view"</span>, <span class="code-quote">"12/0"</span>), 59, 5, 0, Button.Button1)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill.brightness, 0.2)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill.red, 0.2)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill.blue, 0.2)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill.green, 0.2)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill.saturation, 0)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill[<span class="code-quote">"class"</span>], <span class="code-quote">"javafx.scene.paint.Color"</span>)
    test.compare(str(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill), <span class="code-quote">"0x333333ff"</span>)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill.hue, 0)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill.opacity, 1)
    test.compare(waitForObjectExists(<span class="code-quote">":itemTbl_cell indexed-cell table-row-cell"</span>).textfill.opaque, True)

Squish/Qt

Qt has QWidget classes like QLabel that are quite similar to their Java counterparts. This means the text can be rich-text colored from HTML, or colored from properties in the actual QWidget. Qt UIs can also be written in QML, in which case we have Text items with color as a property. Qt’s UI can be styled through QStyle, sometimes determining the text foreground and background colors.

Custom Painting

It is possible to draw text using QPainter in the paintEvent(), which means either a comparison of the QObject property used for color in the paintEvent() function, or a screenshot comparison, is appropriate.

QWidget, QPalette

QLabel, like all QWidget, has a QPalette, or a set of colors for different roles. The palette itself is exposed as a property in Squish. Methods of the QPalette can be called from a test script to get the colors for a given QWidget color role/state.

QAbstractButton also uses the QPalette but has its own ButtonText and Button color roles.

def main():
    startApplication("widgets")
    mouseClick(waitForObject(":This is a simple QPushButton control_QLabel"), 84, 6, 0, Qt.LeftButton)
    label = waitForObject(":This is a simple QPushButton control_QLabel")
    labelTextColor = label.palette.color(QPalette.Text)
    labelTextColor2 = label.palette.text()
    test.compare(labelTextColor, labelTextColor2)
    clickButton(waitForObject(":Press Me!_QPushButton"))
    buttonTextColor = waitForObject(":Press Me!_QPushButton").palette.color(QPalette.ButtonText)
    test.compare(labelTextColor, buttonTextColor)
    mouseClick(waitForObject(":This is a simple QPushButton control_QLabel"), 100, 9, 0, Qt.LeftButton)

QStyle and StyleSheets

Each QWidget has a style(), which can be set on a global or on a widget-basis, possibly from a CSS StyleSheet. The Style may use or override colors from the QPalette, and if a widget changes states, the color can change too.

TODO: Add example verifying colors of styled widgets

QGraphicsTextItem

In a QGraphicsView, if text is rendered using QGraphicsTextItem, then it is possible to inspect the item itself in the scene, via a Visual verification point, that shows the tree of QGraphicsItems and their properties in a QGraphicsScene. As of Squish 6.2, defaultTextColor(), the property, is not exposed as one of the properties in the Visual Verification Points, so a manual verification point must be written to get this value. In Squish 6.3, you can check its box alongside the other properties.

Like QLabel and other text components in Qt, QGraphicsTextItem can also render HTML rich-text.

def main():
    startApplication(<span class="code-quote">"shapes"</span>)
    clickButton(waitForObject(<span class="code-quote">":Add Text..._QPushButton"</span>))
    type(waitForObject(<span class="code-quote">":Text:_QLineEdit"</span>), <span class="code-quote">"Red Text"</span>)
    type(waitForObject(<span class="code-quote">":Text:_QLineEdit"</span>), <span class="code-quote">"<Return>"</span>)
    mouseClick(waitForObject(<span class="code-quote">":Red Text_QGraphicsTextItem"</span>), 57, 16, 0, Qt.LeftButton)
    textColor = waitForObject(<span class="code-quote">":Red Text_QGraphicsTextItem"</span>).defaultTextColor();
    test.compare(textColor.name, <span class="code-quote">"#ff0000"</span>)

QtQuick Text Items

QtQuick has Text items (Implemented in the QQuickText C++ class), with color property exposed to Squish. For verification points, this property can be easily scriptified from the Squish GUI.

ItemViews (Lists, Trees, Tables)

For QTreeView, QListView, and QTableView, it makes sense to examine the data() from the model(). Clicking on a cell in one of these Views gives us a QModelIndex in Application Objects window, and we can pass it into the model’s data(QModelIndex idx, int itemDataRole) method. For a data role, you can pass it a role of Qt::ForegroundRole or Qt::BackgroundRole which returns the foreground or background text color inside a QVariant.

Keep in mind, these values do not need to be set for each item, in which case the default colors from the QPalette are used instead. This means that you must either check for a null return value from data(), or else put the object.convertTo() into a try block.

To get the QColor out of a QVariant, we use qcolor = object.convertTo(variant, "QColor"), which can be done in a try: block if the value may or may not be set.

def main():
    startApplication(<span class="code-quote">"itemviews"</span>)
    tableViewName = <span class="code-quote">"{occurrence='2' type='QTableView' unnamed='1' visible='1' window=':Item Views_MainWindow'}"</span>
    tableView = waitForObject(tableViewName)
    model = tableView.model()
    selectionModel = tableView.selectionModel()
    <span class="code-keyword">for</span> row in range(model.rowCount()):
        <span class="code-keyword">for</span> column in range(model.columnCount()):
            index = model.index(row, column)
            text = model.data(index).toString()
            checked = selected = ""
            checkState = model.data(index, Qt.CheckStateRole).toInt()
            <span class="code-keyword">if</span> checkState == Qt.Checked:
                checked = <span class="code-quote">" +checked"</span>
            <span class="code-keyword">if</span> selectionModel.isSelected(index):
                selected = <span class="code-quote">" +selected"</span>
            fgColorName = ""
            fgColor = model.data(index, Qt.ForegroundRole)
            <span class="code-keyword">try</span>:
                fgColor = object.convertTo(fgColor, <span class="code-quote">"QColor"</span>)
                fgColorName = fgColor.name
            except:
                pass
            test.log(<span class="code-quote">"(%d, %d) '%s'%s%s %s"</span> % (row, column, text, checked,
                                            selected, fgColorName))


Summary

This article shows how text is colored in the Web, Java and Qt toolkits. A future article may cover Windows, Java/SWT, and other APIs that are supported in Squish.