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 51.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.
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:
NameResolver
, wie in Abschnitt 51.1.6 beschriebenClassNameResolver
, wie in Abschnitt 51.1.8 beschriebenGenericClassNameResolver
, wie in Abschnitt 51.1.7 beschriebenFeatureResolver
, wie in Abschnitt 51.1.9 beschriebenExtraFeatureResolver
, wie in Abschnitt 51.1.10 beschriebenItemNameResolver
, wie in Abschnitt 51.1.11 beschriebenItemValueResolver
, wie in Abschnitt 51.1.12 beschriebenInterestingParentResolver
, wie in Abschnitt 51.1.14 beschriebenTooltipResolver
, wie in Abschnitt 51.1.15 beschriebenIdResolver
, wie in Abschnitt 51.1.16 beschriebenEnabledResolver
, wie in Abschnitt 51.1.17 beschriebenVisibilityResolver
, wie in Abschnitt 51.1.18 beschriebenMainTextResolver
, wie in Abschnitt 51.1.19 beschriebenBusyApplicationDetector
, wie in Abschnitt 51.1.23 beschriebenExtraFeatureMatcher
, wie in Abschnitt 51.1.24.1 beschrieben
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, ...): | |
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.
| |
|
|
|
Die über das webResolvers
Modul registrierten Resolver können mittels der
Funktion removeResolver
deregistriert werden.
|
| | void removeAll() | |
|
| void removeResolver(String name) | |
Parameter | name | Der Name, unter dem der Resolver registriert war. | |
|
|
|
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 51.26: SUT-Skript zur Deregistrierung eines Resolvers | |
Gibt eine Liste der Namen der Resolver zurück, die über das webResolvers
Modul
registriert wurden.
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 51.27: Groovy SUT-Skript zur Abfrage registrierter Resolver über das webResolvers Modul | |
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 51.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 51.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 51.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 51.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 51.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 51.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 51.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 51.35: Ein webVisibilityResolver | |
| getMainTextFunc = """
function getMainText(element,text) {
if (text) return text.replace("TO-DO","")
}
"""
webResolvers.addResolver("removeMarkFromText",getMainTextFunc) |
|
| | Beispiel 51.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 51.37: Ein webBusyPaneResolver | |
| function applicationIsBusy() {
const tasks = $specialFramework.waitingTasks();
return tasks && tasks.length() > 0;
}
webResolvers.addResolver("taskChecker",applicationIsBusy); |
|
| | Beispiel 51.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 51.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 51.40: Die Verwendung von addSpecificExtraFeatureMatcher für einen webExtraFeatureMatcher | |