package com.github.markusbernhardt.selenium2library;
import java.io.File;
import java.util.ResourceBundle;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.robotframework.javalib.annotation.Autowired;
import org.robotframework.javalib.library.AnnotationLibrary;
import com.github.markusbernhardt.selenium2library.keywords.BrowserManagement;
import com.github.markusbernhardt.selenium2library.keywords.Cookie;
import com.github.markusbernhardt.selenium2library.keywords.Element;
import com.github.markusbernhardt.selenium2library.keywords.FormElement;
import com.github.markusbernhardt.selenium2library.keywords.JavaScript;
import com.github.markusbernhardt.selenium2library.keywords.Logging;
import com.github.markusbernhardt.selenium2library.keywords.RunOnFailure;
import com.github.markusbernhardt.selenium2library.keywords.Screenshot;
import com.github.markusbernhardt.selenium2library.keywords.SelectElement;
import com.github.markusbernhardt.selenium2library.keywords.TableElement;
import com.github.markusbernhardt.selenium2library.keywords.Waiting;
import com.github.markusbernhardt.selenium2library.utils.Javadoc2Libdoc;
/**
* Selenium2Library is a web testing library for the Robot Framework and was
* originally written in Python. This is the Java port of the Selenium 2
* (WebDriver) Python library for Robot Framework. It uses the Selenium 2
* (WebDriver) libraries internally to control a web browser. See <a
* href="http://seleniumhq.org/docs/03_webdriver.html">WebDriver</a> for more
* information on Selenium 2 and WebDriver. It runs tests in a real browser
* instance and should work with most modern browsers and can be used with the
* Jython interpreter or any other Java application.<br>
* <br>
* <span style="font-size: 120%;"><b>Before running tests</b></span><br>
* Prior to running test cases using Selenium2Library, the library must be
* imported into your Robot Framework test suite (see importing section), and
* the `Open Browser` keyword must be used to open a browser to the desired
* location.<br>
* <br>
* <span style="font-size: 120%;"><b>Locating elements</b></span><br>
* All keywords in Selenium2Library that need to find an element on the page
* take an locator argument.<br>
* <br>
* <b>Key attributes</b><br>
* By default, when a locator value is provided, it is matched against the key
* attributes of the particular element type. The attributes <i>id</i> and
* <i>name</i> are key attributes to all elements.<br>
* <br>
* List of key attributes:
* <table border="1" cellspacing="0" summary="">
* <tr>
* <td><b>Element Type</b></td>
* <td><b>Key Attributes</b></td>
* </tr>
* <tr>
* <td>A</td>
* <td>@id,@name,@href,text</td>
* </tr>
* <tr>
* <td>IMG</td>
* <td>@id,@name,@src,@alt</td>
* </tr>
* <tr>
* <td>INPUT</td>
* <td>@id,@name,@value,@src</td>
* </tr>
* <tr>
* <td>BUTTON</td>
* <td>@id,@name,@value,text</td>
* </tr>
* <tr>
* <td>*</td>
* <td>@id,@name</td>
* </tr>
* </table>
* <br>
* Example:
* <table border="1" cellspacing="0" summary="">
* <tr>
* <td>Click Element</td>
* <td>my_element</td>
* </tr>
* </table>
* <br>
* <b>Locator strategies</b><br>
* It is also possible to specify the approach Selenium2Library should take to
* find an element by specifying a locator strategy with a locator prefix.<br>
* <br>
* Supported strategies are:
* <table border="1" cellspacing="0" summary="">
* <tr>
* <td><b>Strategy</b></td>
* <td><b>Example</b></td>
* <td><b>Description</b></td>
* </tr>
* <tr>
* <td>identifier</td>
* <td>Click Element | identifier=my_element</td>
* <td>Matches by @id or @name attribute</td>
* </tr>
* <tr>
* <td>id</td>
* <td>Click Element | id=my_element</td>
* <td>Matches by @id attribute</td>
* </tr>
* <tr>
* <td>name</td>
* <td>Click Element | name=my_element</td>
* <td>Matches by @name attribute</td>
* </tr>
* <tr>
* <td>xpath</td>
* <td>Click Element | xpath=//div[@id='my_element']</td>
* <td>Matches by arbitrary XPath expression</td>
* </tr>
* <tr>
* <td>dom</td>
* <td>Click Element | dom=document.images[56]</td>
* <td>Matches by arbitrary DOM expression</td>
* </tr>
* <tr>
* <td>link</td>
* <td>Click Element | link=My Link</td>
* <td>Matches by the link text</td>
* </tr>
* <tr>
* <td>css</td>
* <td>Click Element | css=div.my_class</td>
* <td>Matches by CSS selector</td>
* </tr>
* <tr>
* <td>jquery</td>
* <td>Click Element | jquery=div.my_class</td>
* <td>Matches by jQuery/sizzle selector</td>
* </tr>
* <tr>
* <td>sizzle</td>
* <td>Click Element | sizzle=div.my_class</td>
* <td>Matches by jQuery/sizzle selector</td>
* </tr>
* <tr>
* <td>tag</td>
* <td>Click Element | tag=div</td>
* <td>Matches by HTML tag name</td>
* </tr>
* </table>
* <br>
* <b>Locating tables</b><br>
* Table related keywords, such as `Table Should Contain`, work differently. By
* default, when a table locator value is provided, it will search for a table
* with the specified id attribute.<br>
* <br>
* Example:<br>
* <table border="1" cellspacing="0" summary="">
* <tr>
* <td>Table Should Contain</td>
* <td>my_table</td>
* <td>text</td>
* </tr>
* </table>
* <br>
* More complex table locator strategies:
* <table border="1" cellspacing="0" summary="">
* <tr>
* <td><b>Strategy</b></td>
* <td><b>Example</b></td>
* <td><b>Description</b></td>
* </tr>
* <tr>
* <td>xpath</td>
* <td>Table Should Contain | xpath=//table/[@name="my_table"] | text</td>
* <td>Matches by arbitrary XPath expression</td>
* </tr>
* <tr>
* <td>css</td>
* <td>Table Should Contain | css=table.my_class | text</td>
* <td>Matches by CSS selector</td>
* </tr>
* </table>
* <br>
* <b>Custom location strategies</b><br>
* It is also possible to register custom location strategies. See `Add Location
* Strategy` for details about custom location strategies.<br>
* <br>
* Example:
* <table border="1" cellspacing="0" summary="">
* <tr>
* <td>Add Location Strategy</td>
* <td>custom</td>
* <td>return window.document.getElementById(arguments[0]);</td>
* </tr>
* <tr>
* <td>Input Text</td>
* <td>custom=firstName</td>
* <td>Max</td>
* </tr>
* </table>
* <br>
* <span style="font-size: 120%;"><b>Timeouts</b></span><br>
* There are several Wait ... keywords that take <b>timeout</b> as an argument.
* All of these timeout arguments are optional. The timeout used by all of them
* can be set globally using the `Set Selenium Timeout keyword`.<br>
* <br>
* All timeouts can be given as numbers considered seconds (e.g. 0.5 or 42) or
* in Robot Framework's time syntax (e.g. '1.5 seconds' or '1 min 30 s'). See <a
* href=
* "http://robotframework.googlecode.com/svn/trunk/doc/userguide/RobotFrameworkUserGuide.html#time-format"
* >Time Format</a> for details about the time syntax.<br>
* <br>
* <span style="font-size: 120%;"><b>Log Level</b></span><br>
* There are several keywords that take <b>timeout</b> as an argument. All of
* these timeout arguments are optional. The default is usually INFO.<br>
* <br>
* List of log levels:
* <table border="1" cellspacing="0" summary="">
* <tr>
* <td><b>Log Level</b></td>
* <td><b>Description</b></td>
* </tr>
* <tr>
* <td>DEBUG</td>
* <td></td>
* </tr>
* <tr>
* <td>INFO</td>
* <td></td>
* </tr>
* <tr>
* <td>HTML</td>
* <td>Same as INFO, but message is in HTML format</td>
* </tr>
* <tr>
* <td>TRACE</td>
* <td></td>
* </tr>
* <tr>
* <td>WARN</td>
* <td></td>
* </tr>
* </table>
*/
public class Selenium2Library extends AnnotationLibrary {
/**
* The list of keyword patterns for the AnnotationLibrary
*/
public static final String KEYWORD_PATTERN = "com/github/markusbernhardt/selenium2library/keywords/**/*.class";
/**
* The javadoc to libdoc converter
*/
public static final Javadoc2Libdoc JAVADOC_2_LIBDOC = new Javadoc2Libdoc(Selenium2Library.class);
/**
* The library documentation is written in HTML
*/
public static final String ROBOT_LIBRARY_DOC_FORMAT = "HTML";
/**
* The scope of this library is global.
*/
public static final String ROBOT_LIBRARY_SCOPE = "GLOBAL";
/**
* The actual version of this library. Loaded from maven project.
*/
public static final String ROBOT_LIBRARY_VERSION = loadRobotLibraryVersion();
private static String loadRobotLibraryVersion() {
try {
return ResourceBundle.getBundle(Selenium2Library.class.getCanonicalName().replace(".", File.separator))
.getString("version");
} catch (RuntimeException e) {
return "unknown";
}
}
public Selenium2Library() {
this("5.0");
}
public Selenium2Library(String timeout) {
this(timeout, "0.0");
}
public Selenium2Library(String timeout, String implicitWait) {
this(timeout, implicitWait, "Capture Page Screenshot");
}
/**
* Selenium2Library can be imported with optional arguments.<br>
* <br>
* <b>timeout</b> is the default timeout used to wait for all waiting
* actions. It can be changed later with `Set Selenium Timeout`.<br>
* <br>
* <b>implicitWait</b> is the implicit timeout that Selenium waits, when
* looking for elements. It can be changed later with `Set Selenium Implicit
* Wait`. See <a
* href="http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp"
* >WebDriver: Advanced Usage</a> of the SeleniumHQ documentation for
* details about WebDriver's implicit wait functionality.<br>
* <br>
* <b>runOnFailure</b> specifies the name of a keyword (from any available
* libraries) to execute when a Selenium2Library keyword fails. By default
* `Capture Page Screenshot` will be used to take a screenshot of the
* current page. Using the value \"Nothing\" will disable this feature
* altogether. See `Register Keyword To Run On Failure` keyword for details
* about this functionality.<br>
* <br>
* Examples:
* <table border="1" cellspacing="0" summary="">
* <tr>
* <td>Library</td>
* <td>Selenium2Library</td>
* <td></td>
* <td></td>
* <td></td>
* <td></td>
* </tr>
* <tr>
* <td>Library</td>
* <td>Selenium2Library</td>
* <td>15</td>
* <td></td>
* <td></td>
* <td># Sets timeout to 15 seconds</td>
* </tr>
* <tr>
* <td>Library</td>
* <td>Selenium2Library</td>
* <td>0</td>
* <td>5</td>
* <td></td>
* <td># Sets timeout to 0 seconds and implicitWait to 5 seconds</td>
* </tr>
* <tr>
* <td>Library</td>
* <td>Selenium2Library</td>
* <td>0</td>
* <td>5</td>
* <td>Log Source</td>
* <td># Sets timeout to 0 seconds, implicitWait to 5 seconds and runs `Log
* Source` on failure</td>
* </tr>
* <tr>
* <td>Library</td>
* <td>Selenium2Library</td>
* <td>0</td>
* <td>5</td>
* <td>Nothing</td>
* <td># Sets timeout to 0 seconds, implicitWait to 5 seconds and does
* nothing on failure</td>
* </tr>
* </table>
*
* @param timeout
* Default=5.0. Optional custom timeout.
* @param implicitWait
* Default=0.0. Optional custom implicit wait time.
* @param keywordToRunOnFailure
* Default=Capture Page Screenshot. Optional custom keyword to
* run on failure.
*/
public Selenium2Library(String timeout, String implicitWait, String keywordToRunOnFailure) {
super();
addKeywordPattern(KEYWORD_PATTERN);
createKeywordFactory(); // => init annotations
browserManagement.setSeleniumTimeout(timeout);
browserManagement.setSeleniumImplicitWait(implicitWait);
runOnFailure.registerKeywordToRunOnFailure(keywordToRunOnFailure);
}
// ##############################
// Autowired References
// ##############################
/**
* Instantiated BrowserManagement keyword bean
*/
@Autowired
protected BrowserManagement browserManagement;
/**
* Instantiated Cookie keyword bean
*/
@Autowired
protected Cookie cookie;
/**
* Instantiated Element keyword bean
*/
@Autowired
protected Element element;
/**
* Instantiated FormElement keyword bean
*/
@Autowired
protected FormElement formElement;
/**
* Instantiated JavaScript keyword bean
*/
@Autowired
protected JavaScript javaScript;
/**
* Instantiated Logging keyword bean
*/
@Autowired
protected Logging logging;
/**
* Instantiated RunOnFailure keyword bean
*/
@Autowired
protected RunOnFailure runOnFailure;
/**
* Instantiated Screenshot keyword bean
*/
@Autowired
protected Screenshot screenshot;
/**
* Instantiated SelectElement keyword bean
*/
@Autowired
protected SelectElement selectElement;
/**
* Instantiated TableElement keyword bean
*/
@Autowired
protected TableElement tableElement;
/**
* Instantiated Waiting keyword bean
*/
@Autowired
protected Waiting waiting;
// ##############################
// Getter / Setter
// ##############################
public BrowserManagement getBrowserManagement() {
return browserManagement;
}
public Cookie getCookie() {
return cookie;
}
public Element getElement() {
return element;
}
public FormElement getFormElement() {
return formElement;
}
public JavaScript getJavaScript() {
return javaScript;
}
public Logging getLogging() {
return logging;
}
public RunOnFailure getRunOnFailure() {
return runOnFailure;
}
public Screenshot getScreenshot() {
return screenshot;
}
public SelectElement getSelectElement() {
return selectElement;
}
public TableElement getTableElement() {
return tableElement;
}
public Waiting getWaiting() {
return waiting;
}
// ##############################
// Public Methods
// ##############################
@Override
public Object runKeyword(String keywordName, Object[] args) {
return super.runKeyword(keywordName, toStrings(args));
}
@Override
public String getKeywordDocumentation(String keywordName) {
String keywordDocumentation = JAVADOC_2_LIBDOC.getKeywordDocumentation(keywordName);
if (keywordDocumentation == null) {
try {
return super.getKeywordDocumentation(keywordName);
} catch (NullPointerException e) {
return "";
}
}
return keywordDocumentation;
}
/**
* Returns the currently active Selenium2Library instance.
*
* @return the library instance
* @throws ScriptException - if error occurs in script
*/
public static Selenium2Library getLibraryInstance() throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("python");
engine.put("library", "Selenium2Library");
engine.eval("from robot.libraries.BuiltIn import BuiltIn");
engine.eval("instance = BuiltIn().get_library_instance(library)");
return (Selenium2Library) engine.get("instance");
}
// ##############################
// Internal Methods
// ##############################
/**
* Convert all arguments in the object array to string
*
* @param args
* array with object to convert to the return string
* @return the return string
*/
protected Object[] toStrings(Object[] args) {
Object[] newArgs = new Object[args.length];
for (int i = 0; i < newArgs.length; i++) {
if (args[i] == null || args[i].getClass().isArray()) {
newArgs[i] = args[i];
} else {
newArgs[i] = args[i].toString();
}
}
return newArgs;
}
}