cz.datalite.webdriver.ZkDriver Maven / Gradle / Ivy
Show all versions of Selenium Show documentation
package cz.datalite.webdriver;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import cz.datalite.webdriver.components.ZkElement;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.htmlunit.HtmlUnitDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumMap;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* Selenium web driver offers only few methods how to work with page. This
* class coinains it and extends so there are many other possibilities how
* to work with page.
*
* There are enhancements written exactly for ZK framework pages so these
* methods should be fully compatible with this ajax framework. Also there
* are methods which programmers may find very useful. There are implemented
* wait cycles which waits til page is loaded or component is shown. There
* is loaded configuration and all properties are accessible via getters.
*
* This framework is dependent on DataLite application architecture and
* its libraries beacause many rules are written into source-code and demands
* strictly on application architecture. Especially ZkInfrastructure InitListener
* as part of DLComponents which handles framework's ajax requests.
*
* Configuration is loaded from zk-webdriver.properties which is located
* in project. There can be defined properties which can be overwritten
* by enviroment properties. This cascade loading can be used on many
* type of servers like build, testing or application as well as developing,
* testing or production to set different configuration using enviroment
* properties.
*
* Acceptable properties are:
*
* - driver - Defines which core will be used. Options: firefox,
* ie, chrome, htmlunit
* - serverUrl - Location of application, url location,
* eg. http://localhost:8080/MyProject
* - username - Username which is filled into login page.
* - password - Password which is filled into login page
* - timeout - Each request to the server can have time limit.
* Usually responses are ~ms but webdriver is pretty slow so one request
* ussualy takes one second. Timeout is in seconds.
* - fullTest - Full test contains full test of listboxes
* - sorting and filtering. This is slow process so there is
* recommended to set up it only on testing servers.
* - screenOnError - When this framework detects ZK error it can take
* a screenshot. This parameter turns it on, it is off in default.
* Values are true and false.
* - screenshotDir - Taken screenshots have to be stored. There is
* defined location. It can be absolute or relative from the applications
* root. Screenshots are named according to actual
* test class and method, should be store into build's directory because
* there are no timestamps.
* - screenshotUrl - Taken screenshots can generate url and append it
* into exceptions to be visible in testing server outputs'. This url
* is assembled from this part and file name.
*
*
* @author Karel Cemus
* @since version 3, 9.9.2010
*/
public class ZkDriver {
protected static final Logger LOGGER = LoggerFactory.getLogger( "cz.datalite.selenium" );
/** The webDriver instance. */
protected WebDriver webDriver;
/** Wait for instance. */
protected Wait webDriverWait;
/** ZK ID prefix. */
protected String idPrefix;
// Properties
/** The server prefix (to construct URL). */
protected String serverUrl;
/** Username for login screen. */
protected String username;
/** Password for login screen. */
protected String password;
/** Ajax wait timeout. */
protected int timeout;
/** Do full test of each component (slow). */
protected boolean fullTest;
/** driver name */
protected String driver;
/** dir for screens */
protected String screenshotDir;
/** url for screens accessing */
protected String screenshotUrl;
/** defines if take screenshot */
protected boolean screenOnError;
/**
* Key for property map. This also serves as full
* list of acception properties.
*/
private static enum Property {
DRIVER( "driver", "zk.webdriver.driver", "firefox" ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.driver = properties.get( Property.DRIVER );
if ( String.CASE_INSENSITIVE_ORDER.compare( "ie", driver.driver ) == 0 ) {
driver.webDriver = initIEDriver();
} else if ( String.CASE_INSENSITIVE_ORDER.compare( "chrome", driver.driver ) == 0 ) {
driver.webDriver = initChromeDriver();
} else if ( String.CASE_INSENSITIVE_ORDER.compare( "htmlunit", driver.driver ) == 0 ) {
driver.webDriver = initHTMLUnitDriver();
} else {
driver.webDriver = initFirefoxDriver();
}
}
},
SERVER_URL( "server", "zk.webdriver.server", "http://localhost:8787" ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.serverUrl = properties.get( Property.SERVER_URL );
}
},
USERNAME( "username", "zk.webdriver.username", "admin" ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.username = properties.get( Property.USERNAME );
}
},
PASSWORD( "password", "zk.webdriver.password", "" ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.password = properties.get( Property.PASSWORD );
}
},
TIMEOUT( "timeout", "zk.webdriver.timeout", "20" ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.timeout = Integer.parseInt( properties.get( Property.TIMEOUT ) );
}
},
FULL_TEST( "fullTest", "zk.webdriver.fullTest", "false" ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.fullTest = Boolean.valueOf( properties.get( Property.FULL_TEST ) );
}
},
SCREENSHOT_DIR( "screenshotDir", "zk.webdriver.screenshotDir", "." ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.screenshotDir = properties.get( Property.SCREENSHOT_DIR );
}
},
SCREENSHOT_URL( "screenshotUrl", "zk.webdriver.screenshotUrl", "" ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.screenshotUrl = properties.get( Property.SCREENSHOT_URL );
}
},
SCREEN_ON_ERROR( "screenOnError", "zk.webdriver.screenOnError", "false" ) {
@Override
public void set( final ZkDriver driver, final Map properties ) {
driver.screenOnError = Boolean.valueOf( properties.get( Property.SCREEN_ON_ERROR ) );
}
};
/** key in property file */
protected final String propertyKey;
/** key in environment */
protected final String envKey;
/** default value */
protected final String defaultValue;
private Property( final String propertyKey, final String envKey, final String defaultValue ) {
this.propertyKey = propertyKey;
this.envKey = envKey;
this.defaultValue = defaultValue;
}
public String getDefaultValue() {
return defaultValue;
}
public String getEnvKey() {
return envKey;
}
public String getPropertyKey() {
return propertyKey;
}
public abstract void set( final ZkDriver driver, final Map properties );
}
/**
* Loads property ( configuration ) file
* @return loaded properties
*/
private static Properties loadPropertyFile() {
// load property file
final InputStream file = Thread.currentThread().getContextClassLoader().getResourceAsStream( "zk-webdriver.properties" );
if ( file == null ) {
throw new IllegalStateException( "Property file wasn't found." );
} else {
final Properties properties = new Properties();
try {
properties.load( file );
return properties;
} catch ( IOException ex ) {
LOGGER.error("Property file couldn't be loaded", ex );
throw new IllegalStateException( "Property file wasn't loaded." );
}
}
}
/**
* Loads one property from file and from environment and the one which
* higher priority returns. Also accepts default values which are used
* when the property is defined nowhere.
* @param properties Object with loaded properties
* @param env environment properties
* @param key key of property in property file
* @param envKey key of property in environment
* @param defaultValue default value
* @return value with highest priority
*/
private static String loadProperty( final Properties properties, final Map env, final String key, final String envKey, final String defaultValue ) {
// environment variables overwrites properties - usefull for build server
String value = properties.getProperty( key, defaultValue );
if ( env.containsKey( envKey ) ) {
value = env.get( envKey );
}
return value;
}
/**
* Loads properties from file and environment and transforms them into map
* @return loaded properties
*/
private static Map loadProperties() {
final Map propertyMap = new EnumMap( Property.class );
final Properties propertyFile = loadPropertyFile();
final Map env = System.getenv();
for ( Property property : Property.values() ) {
propertyMap.put( property, loadProperty( propertyFile, env, property.getPropertyKey(), property.getEnvKey(), property.getDefaultValue() ) );
}
return propertyMap;
}
private static WebDriver initIEDriver() {
return new InternetExplorerDriver();
}
private static WebDriver initChromeDriver() {
return new ChromeDriver();
}
private static WebDriver initHTMLUnitDriver() {
final HtmlUnitDriver htmlDriver = new HtmlUnitDriver( BrowserVersion.getDefault() );
htmlDriver.setJavascriptEnabled( true );
return htmlDriver;
}
private static WebDriver initFirefoxDriver() {
return new FirefoxDriver();
}
public static ZkDriver init() {
final Map properties = loadProperties();
final ZkDriver zkDriver = new ZkDriver( properties );
ZkElement.setZkDriver( zkDriver );
return zkDriver;
}
protected ZkDriver( final Map properties ) {
for ( Property property : Property.values() ) {
property.set( this, properties );
}
webDriverWait = new WebDriverWait( webDriver, timeout );
}
public boolean isFullTest() {
return fullTest;
}
public String getIdPrefix() {
return idPrefix;
}
public String getPassword() {
return password;
}
public String getServerUrl() {
return serverUrl;
}
public int getTimeout() {
return timeout;
}
public String getUsername() {
return username;
}
public String getScreenshotDir() {
return screenshotDir;
}
public String getScreenshotUrl() {
return screenshotUrl;
}
public boolean isScreenOnError() {
return screenOnError;
}
public WebDriver getWebDriver() {
return webDriver;
}
public Wait getWebDriverWait() {
return webDriverWait;
}
/**
* Execute javascript in the context of the currently selected frame or
* window. This means that "document" will refer to the current document.
* If the script has a return value, then the following steps will be taken:
*
* - For an HTML element, this method returns a WebElement
* - For a number, a Long is returned
* - For a boolean, a Boolean is returned
* - For all other cases, a String is returned.
* - For an array, return a List<Object> with each object
* following the rules above. We support nested lists.
* - Unless the value is null or there is no return value,
* in which null is returned
*
* Arguments must be a number, a boolean, a String, WebElement,
* or a List of any combination of the above. An exception will be
* thrown if the arguments do not meet these criteria. The arguments
* will be made available to the javascript via the "arguments" magic
* variable, as if the function were called via "Function.apply"
*
* @param script The javascript to execute
* @param args The arguments to the script. May be empty
* @return One of Boolean, Long, String, List or WebElement. Or null.
*/
public Object executeScript( final String script, final Object... args ) {
return (( JavascriptExecutor ) webDriver).executeScript( script, args );
}
/**
* Wait for ZK AU requests to finish.
*
* Evaluate javascript: zAu.processing() and waits unitl it returns false or null.
*/
public void waitForProcessing() {
webDriverWait.until( new VisibilityOfElementLocated( By.tagName( "body" ) ) );
webDriverWait.until( new ExpectedCondition() {
public Boolean apply( final WebDriver f ) {
//if ((Boolean) executeScript("if (zAu.processing()) { return true; } else { return false; }")) {
final Object value = executeScript( "return zk.processing;" );
if ( value == null || ( Boolean ) value ) {
return false;
} else {
return true;
}
}
} );
}
public void waitForElement( final String id ) {
waitForElement(By.id( id ));
}
public void waitForElement( final By by ) {
waitForProcessing();
webDriverWait.until( new VisibilityOfElementLocated( by ) );
}
public void waitForElement( final WebElement parent, final By by ) {
waitForProcessing();
webDriverWait.until( new VisibilityOfElementLocated( parent, by ) );
}
public void waitForElementVisible( final By by ) {
waitForProcessing();
webDriverWait.until( new VisibilityOfElementLocated( by ) );
}
/**
* Sleep for amount of time, usefull for some javascript operations without ZK Processing.
* Use carefully, because it may cause unpredictable behaviour, wait for element or waitForProcessing()
* is usually more appropriate.
*
* @param miliseconds time for sleep in miliseconds
*/
public void sleep( final int miliseconds ) {
try {
Thread.sleep( miliseconds );
} catch ( InterruptedException e ) {
Thread.currentThread().interrupt();
throw new IllegalStateException( e );
}
}
/**
* Close the driver and all resources (e.g. firefox window).
*
* Before window is closed, it checks for error window, copy out error message
* and rethrow as java error (thus make test fail).
*/
public void close() {
try {
webDriver.close();
} catch ( Exception ex ) {
// html unit doesn't close correctly, ignore it
}
}
/**
* Load a new web page in the current browser window. It use the Navigate method, for further information see WebDriver#get description.
*
* This method adds two features:
* Address prefix: if the URL doesn't start with "http://", it is automatically prefixed with getServerPrefix().
* Autologin: if the URL loads to login window, it fills login information with properties settings.
*
* @param url the URL to go to.
*
* @see WebDriver#get(String)
*/
public void get( final String url ) {
if ( url.startsWith( "http://" ) ) {
webDriver.get( url );
} else {
webDriver.get( getServerUrl() + url );
}
webDriverWait.until( new VisibilityOfElementLocated( By.tagName( "body" ) ) );
ZkElement.tryLogin();
}
/**
* This method sends command to the server with defined value and
* waits for the response. This is used to translate ids to uuids,
* for completing listboxes etc. On server side has to be InitListener
* from Module ZKDLComponents which handles these requests.
* @param command command which is known on server side
* @param value command parameter
* @return server's response
*/
public String sendCommand( final String command, final String value ) {
executeScript( "zAu.send(new zk.Event(null, 'onDataliteTestService', '" + command + ":" + value + "', 10))" );
waitForProcessing();
return webDriver.findElement( By.className( "selenium-response" ) ).getText();
}
}