Handbuch

47.2
Das webResolvers-Modul

4.2+In den meisten Fällen kann man die Komponenten-Erkennung von QF-Test am besten mit einem klassischen Resolver anpassen, wie im letzen Kapitel Abschnitt 47.1 beschrieben. Wenn der Resolver jedoch für ein Web-Projekt verwendet wird und stark mit dem Browser interagiert, dann bietet sich mit dem webResolvers-Modul die Möglichkeit, den Resolver-Code direkt im Browser auszuführen.

47.2.1
Allgemeine Informationen

Hinweis QF-Test speichert den aktuellen Zustand der Webseite, die aktuell im Browser angezeigt wird, in einem lokalen Cache. Die meisten Methoden, die üblicherweise in einem Resolver verwendet werden, können von QF-Test mit Hilfe dieses Caches ausgeführt werden. Dies führt zu einer wesentlichen Steigerung der Ausführungs-Geschwindigkeit. Resolver, die mit dem webResolvers-Modul registriert werden, kommen immer direkt im Browser zur Ausführung, wodurch die Komponenten-Erkennung verlangsamt werden kann. Allerdings ist es sinnvoll, resolver mit dem webResolvers-Modul zu registrieren, wenn diese grundsätzlich stark auf Browser-Funktionen angewiesen sind, d.h. wenn sie auf Aufrufen der Methoden getAttributeIfSpecified oder evalJS(...) basieren.

Die API des webResolvers-Moduls imitiert diejenige des resolvers-Moduls. Daher haben die vorhandenen Methoden dieselben Namen und werden auch genauso verwendet wie die im letzen Kapitel vorgestellten Methoden. Der wesentliche Unterschied besteht bei der Definition des Resolver-Algorithmus: Hier werden immer JavaScript-Methoden (eingepackt in Strings) erwartet, die dann direkt im Browser ausgeführt werden. Dort erhalten Sie dann auch Browser- und JavaScript-Objekte als Argumente.

HinweisWenn das SUT-Skript, in welchem die Resolver registriert werden, selbst bereits in JavaScript geschrieben ist, so kann man als Argument auch direkt Funktionen angeben. Diese werden dann beim Aufruf in Strings umgewandelt und so im Browser registriert. Da sie dann aber im Browser-Kontext (und nicht in der SUT-Skript-Umgebung von QF-Test) ausgeführt werden, können keine Daten über gemeinsame Variablen zwischen dem registrierenden Skript und der Resolver-Funktion ausgetauscht werden

Das webResolvers-Modul beschränkt sich auf diese Resolver-Typen:

47.2.2
addResolver

Die generische Methode addResolver ist zentral für das webResolvers-Module. Sie registriert einen neuen Resolver. Der Typ wird dabei bestimmt anhand des Namens der JavaScript-Funktion (welcher nicht immer identisch mit dem Name der Variable, in der die Funktion gespeichert wurde) und der Anzahl der von ihr spezifierten Argumente.

 
 
addResolver(String resolverName, String function, Object target=None, ...):
Registriert den oder die über die JavaScript Function festgelegten webResolver für die angegebenen Ziele. Falls bereits ein Resolver unter dem angegebenen Resolver-Namen registriert war, wird dieser zunächst deregistriert.
Parameter
name Der Name unter dem der Resolver registriert werden soll.
function Die JavaScript-Funktion, welche die Methode des Resolvers implementiert. Der Name dieser Methode definiert den Typ des registrierten Resolvers. Zulässige Werte sind z.B.: getName, getClassName, getGenericClassName, getFeature, getExtraFeatures, getItemName, getItemValue, isInterestingParent, getTooltip, getId, isEnabled, isVisible, getMainText, matchExtraFeature und applicationIsBusy.
target Ein oder mehrere optionale Ziele für die der Resolver registriert werden soll. Für jedes Ziel gibt es folgende Varianten:
  • Eine individuelle Komponente
  • Der Name einer Klasse
Ist kein Ziel angegeben, wird der Resolver global für alle Komponenten registriert.
 
 
47.2.3
removeResolver

Die über das webResolvers Modul registrierten Resolver können mittels der Funktion removeResolver deregistriert werden.

 
 
removeResolver(String name)
Deregistriert einen Resolver von allen Zielen, für die er registriert war.
Parameter
nameDer Name, unter dem der Resolver registriert war.
 
removeAll()
Deregistriert alle über das webResolvers Modul registrierten Resolver von allen Zielen, für die sie registriert waren.
 
 

Im Beispiel wird zunächst ein unter dem Namen "myName" registrierter Resolver entfernt, danach alle über das webResolvers-Modul registrierten Resolver.

webResolvers.removeResolver("myName")
webResolvers.removeAll()
Beispiel 47.26:  SUT-Skript zur Deregistrierung eines Resolvers
47.2.4
listNames

Gibt eine Liste der Namen der Resolver zurück, die über das webResolvers Modul registriert wurden.

 
 
List<String> listNames()
Listet die Namen der registrierten Resolver auf.
 
 

Im Beispiel wird überprüft, ob ein spezifischer Resolver registriert wurde. Falls nicht wird dem Protokoll eine Fehlermeldung hinzugefügt.

if (! webResolvers.listNames().contains("specialNames")) {
    rc.logError("Special names resolver not registered!")
}
Beispiel 47.27:  Groovy SUT-Skript zur Abfrage registrierter Resolver über das webResolvers Modul
47.2.5
Beispiele

Die Benutzung der webResolver ist identisch zu Resolvern, welche über das resolvers-Modul registriert werden. Daher wird hier auf eine ausführliche Beschreibung der Resolver selbst und ihrer Funktionalität verzichtet und auf das vorangehende Kapitel verwiesen. Statt dessen wird hier durch ausgewählte Beispiele die Anwendung des webResolvers-Moduls dargestellt:

getNameFunc = """
function getName(node, name) {
    if (! name) return node.getAttribute("__name__");
}
"""

webResolvers.addResolver("myNames", getNameFunc, "DIV")
Beispiel 47.28:  Ein webNameResolver wird mit Jython registriert
def getFeatureFunc = """
function getFeature(node, feature) {
    try {
        var title = node.firstChild.textContent;
        if (title) return title;
    } catch (e) {}
}
"""

webResolvers.addResolver("paneltitle", getFeatureFunc, "Panel")
Beispiel 47.29:  Ein webFeatureResoler, registriert mit einem Groovy SUT-Skript
function getExtraFeatures(node, features) {
    try {
        const myId = node.getAttribute("myId");
        if (myId) {
            features.add(ExtraFeature.STATE_MUST_MATCH,"myId", myId);
            return features;
        }
    } catch (e) {}
}

webResolvers.addResolver("myId", getExtraFeatures);
Beispiel 47.30:   Ein webExtraFeatureResolver welcher ein 'Weiteres Merkmal' für ein spezifisches Attribut hinzufügt, registriert mit einem JavaScript SUT-Skript
function getExtraFeatures(node, features) {
    const labelFeature == features.get("qfs:label");
    if (labelFeature && labelFeature.value == "bad name") {
        labelFeature.value = "good name";
        return features;
    }
}
webResolvers.addResolver("get label example", getExtraFeatures,"TextField");
Beispiel 47.31:   Ein webExtraFeatureResolver welcher ein bestehendes 'Weiteres Merkmal' ändert
getColumnIdFunc = """
function getColumnId(table, item, name) {
    var id;
    if (item[1] < 0 ) { // whole column addressed
        id = table.getElementsByTagName("TH")[0].id;
    }
    if (id) return id;
}
"""

webResolvers.addResolver("tableColumnId", getColumnIdFunc, "TableColumn")
Beispiel 47.32:  Ein webItemNameResolver, registriert mit einem Jython SUT-Skript
def getTooltipFunc = """
function getTooltip(node, tooltip) {
    if (tooltip) {
        var userLang = navigator.language || navigator.userLanguage;
        if (userLang) {
            return tooltip + "-" + userLang;
        }
    }
}
"""

webResolvers.addResolver("tootip-lang", getTooltipFunc)
Beispiel 47.33:  Ein webTooltipResoler, registriert mit einem Groovy SUT-Skript
function isEnabled(node) {
    try {
        return ! node.className.split(' ').contains("v-disabled");
    } catch (e) {
        return true;
    }
}

webResolvers.addResolver("vEnabledResolver",isEnabledFunc);
Beispiel 47.34:  Ein webEnabledResolver, registriert mit einem JavaScript SUT-Skript
function isVisible(element, visible) {
    const getOpacity = function(el) { // inner function, so it is part
                                           // of the registered resolver
        const style = window.getComputedStyle(el);
        const opacity = style.getPropertyValue("opacity")
        if (! opacity) return 1;
        return parseFloat(opacity);
    }
    return visible && getOpacity(element) > 0;
}

webResolvers.addResolver("opacityResolver",isVisible);
Beispiel 47.35:  Ein webVisibilityResolver
getMainTextFunc = """
function getMainText(element,text) {
    if (text) return text.replace("TO-DO","")
}
"""

webResolvers.addResolver("removeMarkFromText",getMainTextFunc)
Beispiel 47.36:  Ein webMainTextResolver, registriert mit einem Jython SUT-Skript
function isBusy(element) {
    const overlay = element.getElementsByClassName("overlay")[0];
    return !!overlay;
}

webResolvers.addResolver("overlayBusyResolver",isBusy);
Beispiel 47.37:  Ein webBusyPaneResolver
function applicationIsBusy() {
    const tasks = $specialFramework.waitingTasks();
    return tasks && tasks.length() > 0;
}

webResolvers.addResolver("taskChecker",applicationIsBusy);
Beispiel 47.38:  Ein webBusyApplicationDetector
function matchExtraFeature(element, name, value, regexp, negate) {
    if (name != "my:label") return;
    const label = element.getAttribute("my-label");
    var match = false;
    if (label) {
        if (regexp) {
           match = !! label.match(value);
        } else {
           match = (value == label);
        }
    }
    return negate ? ! match : match;
}

webResolvers.addResolver("myLabelResolver", matchExtraFeature)
Beispiel 47.39:  Ein webExtraFeatureMatcher
function matchExtraFeature(element, name, value, regexp, negate) {
    const label = element.getAttribute("my-label");
    var match = false;
    if (label) {
        if (regexp) {
           match = !! label.match(value);
        } else {
           match = (value == label);
        }
    }
    return negate ? ! match : match;
}

webResolvers.addSpecificExtraFeatureMatcher("myLabelResolver",
                               matchExtraFeature, "my:label");
Beispiel 47.40:  Die Verwendung von addSpecificExtraFeatureMatcher für einen webExtraFeatureMatcher