4.2+For most use cases, registering a resolver using the resolvers
module
as described in the last chapter section 52.1 is the best way to
adapt the component recognition of QF-Test.
If the custom resolvers are used for a web context and heavily interacting with the
browser content directly, the webResolvers
module provides an option to run
the resolver code directly in the browser.
Note
QF-Test internally caches the state of the displayed web page and runs most of the commands, which
are usually invoked in a resolver, on the cached representation of the web page.
This tremendously improves resolving time. Resolvers registered on the webResolver
module are always executed directly in the browser, which might slow down component recognition.
On the other hand, resolvers registered using the webResolvers
module are convenient if it requires
several downcalls to the browser, i.e. if using the getAttributeIfSpecified
method or
evalJS(...)
.
In general, the webResolvers
module mimics the resolvers
module, so its
methods and their usage is similar to those described in the last chapter. In difference to those,
the methods have to be written as JavaScript functions, wrapped in Strings. The resolver code is then
directly executed in the browser, so its function arguments are also Browser objects and JavaScript objects.
NoteIf you write your SUT script in JavaScript, you can use a function directly as
argument for the register call. The given function will then be stringified and registered as resolver in
the browser. Be aware that this resolver function is executed in the browser, not in the QF-Test
SUT script environment, and variables are not shared between the two contexts.
The webResolvers
module is limited to these resolvers types:
The generic method addResolver
has a central role in the
webResolvers
module. The method registers a new resolver.
It identifies the type of the resolver using the name of the JavaScript function
which not always is equal to the name of the variable which references the function) and its parameter count.
|
| | addResolver(String resolverName, String function, Object target=None, ...): | |
Parameters | name |
The name under which to register the resolver.
| function |
The JavaScript function implementing the resolver. The name of the function defines the
type of the registered resolver.
Valid names are e.g.: getName , getClassName ,
getGenericClassName , getFeature , getExtraFeatures ,
getItemName , getItemValue , isInterestingParent ,
getTooltip , getId , isEnabled , isVisible ,
getMainText , matchExtraFeature , and applicationIsBusy .
| target |
One or more optional targets to register the resolver for. Each can be any of the following:
- An individual component
- The fully qualified tag name of a class
If no target is given a global resolver for all components is registered.
| |
|
|
|
The function removeResolver
may be used to deregister resolvers installed
via the webResolvers
module.
|
| | void removeAll() | |
|
| void removeResolver(String name) | |
Parameters | name | The name the resolver was registered under. | |
|
|
|
The example first removes a resolver registered under the name "myName", then
deregisters all resolvers registered via the webResolvers
module.
| webResolvers.removeResolver("myName")
webResolvers.removeAll() |
|
| | Example 52.29: SUT script deregistering webResolvers | |
Return a list of resolver names registered via the webResolvers
module.
The example checks whether a certain resolver has been registered. If not, an error
message is written to the run log.
| if (! webResolvers.listNames().contains("specialNames")) {
rc.logError("Special names resolver not registered!")
} |
|
| | Example 52.30: Groovy SUT script searching for a certain resolver registered via the webResolvers module | |
Since the role and usage of webResolvers is equal to those registered using the
resolvers
module, we refer to the last chapter for a detailed description of the
specific resolvers. Here, we demonstrate the usage of webResolvers in selected examples:
| getNameFunc = """
function getName(node, name) {
if (! name) return node.getAttribute("__name__");
}
"""
webResolvers.addResolver("myNames", getNameFunc, "DIV") |
|
| | Example 52.31: Registering a webNameResolver in Jython | |
| def getFeatureFunc = """
function getFeature(node, feature) {
try {
var title = node.firstChild.textContent;
if (title) return title;
} catch (e) {}
}
"""
webResolvers.addResolver("paneltitle", getFeatureFunc, "Panel") |
|
| | Example 52.32: A webFeatureResoler registered using a Groovy SUT script | |
| 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); |
|
| | Example 52.33:
webExtraFeatureResolver adding an 'Extra feature' for a special attribute using a JavaScript SUT script
| |
| 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"); |
|
| | Example 52.34:
webExtraFeatureResolver changing an existing 'Extra feature'
| |
| 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") |
|
| | Example 52.35: A webItemNameResolver registered using a Jython SUT script | |
| def getTooltipFunc = """
function getTooltip(node, tooltip) {
if (tooltip) {
var userLang = navigator.language || navigator.userLanguage;
if (userLang) {
return tooltip + "-" + userLang;
}
}
}
"""
webResolvers.addResolver("tootip-lang", getTooltipFunc) |
|
| | Example 52.36: A webTooltipResoler registered using a Groovy SUT script | |
| function isEnabled(node) {
try {
return ! node.className.split(' ').contains("v-disabled");
} catch (e) {
return true;
}
}
webResolvers.addResolver("vEnabledResolver",isEnabledFunc); |
|
| | Example 52.37: A webEnabledResolver , registered using a JavaScript SUT script | |
| 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); |
|
| | Example 52.38: A webVisibilityResolver | |
| getMainTextFunc = """
function getMainText(element,text) {
if (text) return text.replace("TO-DO","")
}
"""
webResolvers.addResolver("removeMarkFromText",getMainTextFunc) |
|
| | Example 52.39: A webMainTextResolver , registered in a Jython SUT script | |
| function isBusy(element) {
const overlay = element.getElementsByClassName("overlay")[0];
return !!overlay;
}
webResolvers.addResolver("overlayBusyResolver",isBusy); |
|
| | Example 52.40: A webBusyPaneResolver | |
| function applicationIsBusy() {
const tasks = $specialFramework.waitingTasks();
return tasks && tasks.length() > 0;
}
webResolvers.addResolver("taskChecker",applicationIsBusy); |
|
| | Example 52.41: A 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) |
|
| | Example 52.42: A 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"); |
|
| | Example 52.43: Using addSpecificExtraFeatureMatcher for a webExtraFeatureMatcher | |