Vor einigen Tagen hatte ich das Problem, dass ich eine Webanwendung mit einem jscookMenu als zentrale Navigation mit Selenium testen wollte. Das Menü stammte hierbei aus den MyFaces Tomahawk, wurde also via JavaServerFaces (JSF) generiert. Das Problem hierbei war, dass nirgendwo eindeutige IDs im HTML-Quelltext vorhanden waren, um ein Menüitem eindeutig zu identifizieren. Das Einfügen von IDs war auch nicht möglich, da JSF diese einfach nicht mit rendert. Auch die anderen Selenium Standard Locators führten nicht zum Ziel. Einzig über den XPath, augehend vom div-Tag des Menüs, konnte ich mit Selenium einen Klick auf ein Menüitem durchführen. Leider brachte mich das nicht wirklich weiter, da das Menü dynamisch ist und immer mal wieder Menüitems dazukommen oder verschwinden, abhängig von den Einstellungen in der Webanwendung.
Die Lösung: Ich habe einen eigenen Selenium Locator geschrieben, der es mir nun erlaubt jedes Menüitem anzuklicken, einfach anhand der Aufschrift (Label). Den Locator habe ich einfach als Selenium User Extension in JavaScript implementiert.
/*
* Adds an own locator for the jscookMenu menu items to selenium
* due to missing IDs in the menu.
* Returns the first found menu item.
* The running order is:
* - all parent Items
* - all parent subfolders
* - all subfolder items
* - all subfolder subfolders
*
* Usage from JavaSource e.g.:
* selenium.mouseUp("menuitem=Impressum");
* Do not use selenium.click(), not working in FF
* or selenium.clickAt(), very slow in IE!
*
* Author: Thomas Klumpp
*/
PageBot.prototype.locateElementByMenuItem = function(text, inDocument) {
/*
* Returns all elements with the given class name.
* Firefox has a native method (same name) for that since version 3.
* Internet Explorer has this native method since version 9.
* So we better use or own implementation to ensure, that its running everywhere.
* In addition, the return type of these native methods is a NodeList (live collection).
* We are using an array for easy concatenation here.
*/
getElementsByClassName = function(className)
{
var hasClassName = new RegExp('(?:^|\\s)' + className + '(?:$|\\s)');
var allElements = inDocument.getElementsByTagName('*');
var results = [];
var element;
for (var i = 0; (element = allElements[i]) != null; i++) {
var elementClass = element.className;
if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass))
results.push(element);
}
return results;
}
//getting all different menu item types
var mainItems = getElementsByClassName('ThemeOfficeMainItemText');
var mainFolderItems = getElementsByClassName('ThemeOfficeMainFolderText');
var menuItems = getElementsByClassName('ThemeOfficeMenuItemText');
var menuFolderItems = getElementsByClassName('ThemeOfficeMenuFolderText');
//concatenation
var allItems = mainItems.concat(mainFolderItems).concat(menuItems).concat(menuFolderItems);
for (var i = 0; i < allItems.length; i++) {
var currentElement = allItems[i].firstChild;
if (currentElement.data == text) {
//return first found item parent
return currentElement.parentNode;
}
}
//nothing found
return null;
};
Zur Verwendung in der Selenium IDE, kann man das JavaScript File einfach über die Optionen einbinden. Zur Verwendung aus Java mit Selenium RC, einfach den Server mit dem Parameter -userExtension starten:
java -jar selenium-server.jar -userExtensions user-extensions.js
Verwendet werden kann der Locator z.B. dann so:
selenium.mouseUp("menuitem=Impressum");
menuitem= ergibt sich aus dem JavaScript Methodenname.



