blob: 6592420dd65ee4cc13d7bfcfb8b2303344decf89 [file] [log] [blame]
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.tapestry5.test;
import com.thoughtworks.selenium.CommandProcessor;
import com.thoughtworks.selenium.Selenium;
import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
import com.thoughtworks.selenium.webdriven.WebDriverCommandProcessor;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.internal.WrapsDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.ITestContext;
import org.testng.annotations.*;
import org.testng.xml.XmlTest;
import io.github.bonigarcia.wdm.FirefoxDriverManager;
import java.io.File;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
* Base class for creating Selenium-based integration test cases. This class implements all the
* methods of {@link Selenium} and delegates to an instance (setup once per test by
* {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)}.
*
* @since 5.2.0
*/
public abstract class SeleniumTestCase extends Assert implements Selenium
{
public final static Logger LOGGER = LoggerFactory.getLogger(SeleniumTestCase.class);
/**
* 15 seconds
*/
public static final String PAGE_LOAD_TIMEOUT = "15000";
public static final String TOMCAT_6 = "tomcat6";
public static final String JETTY_7 = "jetty7";
/**
* An XPath expression for locating a submit element (very commonly used
* with {@link #clickAndWait(String)}.
*
* @since 5.3
*/
public static final String SUBMIT = "//input[@type='submit']";
/**
* The underlying {@link Selenium} instance that all the methods of this class delegate to;
* this can be useful when attempting to use SeleniumTestCase with a newer version of Selenium which
* has added some methods to the interface. This field will not be set until the test case instance
* has gone through its full initialization.
*
* @since 5.3
*/
@Deprecated
protected Selenium selenium;
protected WebDriver webDriver;
private String baseURL;
private ErrorReporter errorReporter;
private ITestContext testContext;
/**
* Starts up the servers for the entire test (i.e., for multiple TestCases). By placing <parameter> elements
* inside the appropriate <test> (of your testng.xml configuration
* file), you can change the configuration or behavior of the servers. It is common to have two
* or more identical tests that differ only in terms of the <code>tapestry.browser-start-command</code> parameter,
* to run tests against multiple browsers.
* <table>
* <tr>
* <th>Parameter</th>
* <th>Name</th>
* <th>Default</th>
* <th>Description</th>
* </tr>
* <tr>
* <td>container</td>
* <td>tapestry.servlet-container</td>
* <td>JETTY_7</td>
* <td>The Servlet container to use for the tests. Currently {@link #JETTY_7} or {@link #TOMCAT_6}</td>
* </tr>
* <tr>
* <td>webAppFolder</td>
* <td>tapestry.web-app-folder</td>
* <td>src/main/webapp</td>
* <td>Location of web application context</td>
* </tr>
* <tr>
* <td>contextPath</td>
* <td>tapestry.context-path</td>
* <td><em>empty string</em></td>
* <td>Context path (defaults to root). As elsewhere, the context path should be blank, or start with a slash (but
* not end with one).</td>
* </tr>
* <tr>
* <td>port</td>
* <td>tapestry.port</td>
* <td>9090</td>
* <td>Port number for web server to listen to</td>
* </tr>
* <tr>
* <td>sslPort</td>
* <td>tapestry.ssl-port</td>
* <td>8443</td>
* <td>Port number for web server to listen to for secure requests</td>
* </tr>
* <tr>
* <td>browserStartCommand</td>
* <td>tapestry.browser-start-command</td>
* <td>*firefox</td>
* <td>Command string used to launch the browser, as defined by Selenium</td>
* </tr>
* <caption>Options and defaults</caption>
* </table>
*
* Tests in the <em>beforeStartup</em> group will be run before the start of Selenium. This can be used to
* programmatically override the above parameter values.
*
* This method will be invoked in <em>each</em> subclass, but is set up to only startup the servers once (it checks
* the {@link ITestContext} to see if the necessary keys are already present).
*
* @param testContext
* Used to share objects between the launcher and the test suites
* @throws Exception
*/
@BeforeTest(dependsOnGroups =
{"beforeStartup"})
public void testStartup(final ITestContext testContext, XmlTest xmlTest) throws Exception
{
// This is not actually necessary, because TestNG will only invoke this method once
// even when multiple test cases within the test extend from SeleniumTestCase. TestNG
// just invokes it on the "first" TestCase instance it has test methods for.
if (testContext.getAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE) != null)
{
return;
}
// If a parameter is overridden in another test method, TestNG won't pass the
// updated value via a parameter, but still passes the original (coming from testng.xml or the default).
// Seems like a TestNG bug.
// Map<String, String> testParameters = xmlTest.getParameters();
TapestryTestConfiguration annotation = this.getClass().getAnnotation(TapestryTestConfiguration.class);
if (annotation == null)
{
@TapestryTestConfiguration
final class EmptyInnerClass
{
}
annotation = EmptyInnerClass.class.getAnnotation(TapestryTestConfiguration.class);
}
String webAppFolder = getParameter(xmlTest, TapestryTestConstants.WEB_APP_FOLDER_PARAMETER,
annotation.webAppFolder());
String container = getParameter(xmlTest, TapestryTestConstants.SERVLET_CONTAINER_PARAMETER,
annotation.container());
String contextPath = getParameter(xmlTest, TapestryTestConstants.CONTEXT_PATH_PARAMETER,
annotation.contextPath());
int port = getIntParameter(xmlTest, TapestryTestConstants.PORT_PARAMETER, annotation.port());
int sslPort = getIntParameter(xmlTest, TapestryTestConstants.SSL_PORT_PARAMETER, annotation.sslPort());
String browserStartCommand = getParameter(xmlTest, TapestryTestConstants.BROWSER_START_COMMAND_PARAMETER,
annotation.browserStartCommand());
String baseURL = String.format("http://localhost:%d%s/", port, contextPath);
String sep = System.getProperty("line.separator");
LOGGER.info("Starting SeleniumTestCase:" + sep +
" currentDir: " + System.getProperty("user.dir") + sep +
" webAppFolder: " + webAppFolder + sep +
" container: " + container + sep +
" contextPath: " + contextPath + sep +
String.format(" ports: %d / %d", port, sslPort) + sep +
" browserStart: " + browserStartCommand + sep +
" baseURL: " + baseURL);
final Runnable stopWebServer = launchWebServer(container, webAppFolder, contextPath, port, sslPort);
FirefoxDriverManager.getInstance().setup();
File ffProfileTemplate = new File(TapestryRunnerConstants.MODULE_BASE_DIR, "src/test/conf/ff_profile_template");
DesiredCapabilities desiredCapabilities = DesiredCapabilities.firefox();
desiredCapabilities.setCapability(FirefoxDriver.MARIONETTE, true);
FirefoxOptions options = new FirefoxOptions(desiredCapabilities);
if (ffProfileTemplate.isDirectory())
{
FirefoxProfile profile = new FirefoxProfile(ffProfileTemplate);
options.setProfile(profile);
}
FirefoxDriver driver = new FirefoxDriver(options);
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
CommandProcessor webDriverCommandProcessor = new WebDriverCommandProcessor(baseURL, driver);
final ErrorReporterImpl errorReporter = new ErrorReporterImpl(driver, testContext);
ErrorReportingCommandProcessor commandProcessor = new ErrorReportingCommandProcessor(webDriverCommandProcessor,
errorReporter);
Selenium selenium = new WebDriverBackedSelenium(driver, baseURL);
testContext.setAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE, baseURL);
testContext.setAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE, selenium);
testContext.setAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE, errorReporter);
testContext.setAttribute(TapestryTestConstants.COMMAND_PROCESSOR_ATTRIBUTE, commandProcessor);
testContext.setAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE, new Runnable()
{
@Override
public void run()
{
try
{
LOGGER.info("Shutting down selenium client ...");
try
{
selenium.stop();
} catch (RuntimeException e)
{
LOGGER.error("Selenium client shutdown failure.", e);
}
LOGGER.info("Shutting down webdriver ...");
try
{
if (webDriver != null) { // is sometimes null... but why?
webDriver.quit();
}
} catch (RuntimeException e)
{
LOGGER.error("Webdriver shutdown failure.", e);
}
LOGGER.info("Shutting down selenium server ...");
LOGGER.info("Shutting web server ...");
try
{
stopWebServer.run();
} catch (RuntimeException e)
{
LOGGER.error("Web server shutdown failure.", e);
}
// Output, at the end of the Test, any html capture or screen shots (this makes it much easier
// to locate them at the end of the run; there's such a variance on where they end up based
// on whether the tests are running from inside an IDE or via one of the command line
// builds.
errorReporter.writeOutputPaths();
} finally
{
testContext.removeAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE);
testContext.removeAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE);
testContext.removeAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE);
testContext.removeAttribute(TapestryTestConstants.COMMAND_PROCESSOR_ATTRIBUTE);
testContext.removeAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE);
}
}
});
}
private final String getParameter(XmlTest xmlTest, String key, String defaultValue)
{
String value = xmlTest.getParameter(key);
return value != null ? value : defaultValue;
}
private final int getIntParameter(XmlTest xmlTest, String key, int defaultValue)
{
String value = xmlTest.getParameter(key);
return value != null ? Integer.parseInt(value) : defaultValue;
}
/**
* Like {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)} , this may
* be called multiple times against multiple instances, but only does work the first time.
*/
@AfterTest
public void testShutdown(ITestContext context)
{
// Likewise, this method should only be invoked once.
Runnable r = (Runnable) context.getAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE);
// This test is still useful, however, because testStartup() may not have completed properly,
// and the runnable is the last thing it puts into the test context.
if (r != null)
{
LOGGER.info("Shutting down integration test support ...");
r.run();
}
}
/**
* Invoked from {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)} to launch the web
* server to be tested. The return value is a Runnable that can be invoked later to cleanly shut down the launched
* server at the end of the test.
*
* @param container
* identifies which web server should be launched
* @param webAppFolder
* path to the web application context
* @param contextPath
* the path the context is mapped to, usually the empty string
* @param port
* the port number the server should handle
* @param sslPort
* the port number on which the server should handle secure requests
* @return Runnable used to shut down the server
* @throws Exception
*/
protected Runnable launchWebServer(String container, String webAppFolder, String contextPath, int port, int sslPort)
throws Exception
{
final ServletContainerRunner runner = createWebServer(container, webAppFolder, contextPath, port, sslPort);
return new Runnable()
{
@Override
public void run()
{
runner.stop();
}
};
}
private ServletContainerRunner createWebServer(String container, String webAppFolder, String contextPath, int port, int sslPort) throws Exception
{
if (TOMCAT_6.equals(container))
{
return new TomcatRunner(webAppFolder, contextPath, port, sslPort);
}
if (JETTY_7.equals(container))
{
return new JettyRunner(webAppFolder, contextPath, port, sslPort);
}
throw new RuntimeException("Unknown servlet container: " + container);
}
@BeforeClass
public void setup(ITestContext context)
{
this.testContext = context;
selenium = (Selenium) context.getAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE);
webDriver = ((WrapsDriver) selenium).getWrappedDriver();
baseURL = (String) context.getAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE);
errorReporter = (ErrorReporter) context.getAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE);
}
@AfterClass
public void cleanup()
{
selenium = null;
baseURL = null;
errorReporter = null;
testContext = null;
}
/**
* Delegates to {@link ErrorReporter#writeErrorReport(String)} to capture the current page markup in a
* file for later analysis.
*/
protected void writeErrorReport(String reportText)
{
errorReporter.writeErrorReport(reportText);
}
/**
* Returns the base URL for the application. This is of the typically <code>http://localhost:9999/</code> (i.e., it
* includes a trailing slash).
*
* Generally, you should use {@link #openLinks(String...)} to start from your application's home page.
*/
public String getBaseURL()
{
return baseURL;
}
@BeforeMethod
public void indicateTestMethodName(Method testMethod)
{
LOGGER.info("Executing " + testMethod);
testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, testMethod);
String className = testMethod.getDeclaringClass().getSimpleName();
String testName = testMethod.getName().replace("_", " ");
selenium.setContext(className + ": " + testName);
}
@AfterMethod
public void cleanupTestMethod()
{
testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, null);
}
// ---------------------------------------------------------------------
// Start of delegate methods
//
// When upgrading to a new version of Selenium, it is probably easiest
// to delete all these methods and use the Generate Delegate Methods
// refactoring.
// ---------------------------------------------------------------------
@Override
public void addCustomRequestHeader(String key, String value)
{
selenium.addCustomRequestHeader(key, value);
}
@Override
public void addLocationStrategy(String strategyName, String functionDefinition)
{
selenium.addLocationStrategy(strategyName, functionDefinition);
}
@Override
public void addScript(String scriptContent, String scriptTagId)
{
selenium.addScript(scriptContent, scriptTagId);
}
@Override
public void addSelection(String locator, String optionLocator)
{
selenium.addSelection(locator, optionLocator);
}
@Override
public void allowNativeXpath(String allow)
{
selenium.allowNativeXpath(allow);
}
@Override
public void altKeyDown()
{
selenium.altKeyDown();
}
@Override
public void altKeyUp()
{
selenium.altKeyUp();
}
@Override
public void answerOnNextPrompt(String answer)
{
selenium.answerOnNextPrompt(answer);
}
@Override
public void assignId(String locator, String identifier)
{
selenium.assignId(locator, identifier);
}
@Override
public void attachFile(String fieldLocator, String fileLocator)
{
selenium.attachFile(fieldLocator, fileLocator);
}
@Override
public void captureEntirePageScreenshot(String filename, String kwargs)
{
selenium.captureEntirePageScreenshot(filename, kwargs);
}
@Override
public String captureEntirePageScreenshotToString(String kwargs)
{
return selenium.captureEntirePageScreenshotToString(kwargs);
}
@Override
public String captureNetworkTraffic(String type)
{
return selenium.captureNetworkTraffic(type);
}
@Override
public void captureScreenshot(String filename)
{
selenium.captureScreenshot(filename);
}
@Override
public String captureScreenshotToString()
{
return selenium.captureScreenshotToString();
}
@Override
public void check(String locator)
{
WebElement element = webDriver.findElement(convertLocator(locator));
if (!element.isSelected())
{
scrollIntoView(element);
element.click();
}
}
@Override
public void chooseCancelOnNextConfirmation()
{
selenium.chooseCancelOnNextConfirmation();
}
@Override
public void chooseOkOnNextConfirmation()
{
selenium.chooseOkOnNextConfirmation();
}
@Override
public void click(String locator)
{
WebElement element = webDriver.findElement(convertLocator(locator));
scrollIntoView(element);
JavascriptExecutor executor = (JavascriptExecutor)webDriver;
executor.executeScript("arguments[0].click();", element);
// element.click(); // failing as of Aug 2018
}
@Override
public void clickAt(String locator, String coordString)
{
selenium.clickAt(locator, coordString);
}
@Override
public void close()
{
selenium.close();
}
@Override
public void contextMenu(String locator)
{
selenium.contextMenu(locator);
}
@Override
public void contextMenuAt(String locator, String coordString)
{
selenium.contextMenuAt(locator, coordString);
}
@Override
public void controlKeyDown()
{
selenium.controlKeyDown();
}
@Override
public void controlKeyUp()
{
selenium.controlKeyUp();
}
@Override
public void createCookie(String nameValuePair, String optionsString)
{
selenium.createCookie(nameValuePair, optionsString);
}
@Override
public void deleteAllVisibleCookies()
{
selenium.deleteAllVisibleCookies();
}
@Override
public void deleteCookie(String name, String optionsString)
{
selenium.deleteCookie(name, optionsString);
}
@Override
public void deselectPopUp()
{
selenium.deselectPopUp();
}
@Override
public void doubleClick(String locator)
{
selenium.doubleClick(locator);
}
@Override
public void doubleClickAt(String locator, String coordString)
{
selenium.doubleClickAt(locator, coordString);
}
@Override
public void dragAndDrop(String locator, String movementsString)
{
selenium.dragAndDrop(locator, movementsString);
}
@Override
public void dragAndDropToObject(String locatorOfObjectToBeDragged, String locatorOfDragDestinationObject)
{
selenium.dragAndDropToObject(locatorOfObjectToBeDragged, locatorOfDragDestinationObject);
}
@Override
public void dragdrop(String locator, String movementsString)
{
selenium.dragdrop(locator, movementsString);
}
@Override
public void fireEvent(String locator, String eventName)
{
selenium.fireEvent(locator, eventName);
}
@Override
public void focus(String locator)
{
selenium.focus(locator);
}
@Override
public String getAlert()
{
return selenium.getAlert();
}
@Override
public String[] getAllButtons()
{
return selenium.getAllButtons();
}
@Override
public String[] getAllFields()
{
return selenium.getAllFields();
}
@Override
public String[] getAllLinks()
{
return selenium.getAllLinks();
}
@Override
public String[] getAllWindowIds()
{
return selenium.getAllWindowIds();
}
@Override
public String[] getAllWindowNames()
{
return selenium.getAllWindowNames();
}
@Override
public String[] getAllWindowTitles()
{
return selenium.getAllWindowTitles();
}
@Override
public String getAttribute(String attributeLocator)
{
return selenium.getAttribute(attributeLocator);
}
@Override
public String[] getAttributeFromAllWindows(String attributeName)
{
return selenium.getAttributeFromAllWindows(attributeName);
}
@Override
public String getBodyText()
{
return selenium.getBodyText();
}
@Override
public String getConfirmation()
{
return selenium.getConfirmation();
}
@Override
public String getCookie()
{
return selenium.getCookie();
}
@Override
public String getCookieByName(String name)
{
return selenium.getCookieByName(name);
}
@Override
public Number getCursorPosition(String locator)
{
return selenium.getCursorPosition(locator);
}
@Override
public Number getElementHeight(String locator)
{
return selenium.getElementHeight(locator);
}
@Override
public Number getElementIndex(String locator)
{
return selenium.getElementIndex(locator);
}
@Override
public Number getElementPositionLeft(String locator)
{
return selenium.getElementPositionLeft(locator);
}
@Override
public Number getElementPositionTop(String locator)
{
return selenium.getElementPositionTop(locator);
}
@Override
public Number getElementWidth(String locator)
{
return selenium.getElementWidth(locator);
}
@Override
public String getEval(String script)
{
return selenium.getEval(script);
}
@Override
public String getExpression(String expression)
{
return selenium.getExpression(expression);
}
@Override
public String getHtmlSource()
{
return selenium.getHtmlSource();
}
@Override
public String getLocation()
{
return selenium.getLocation();
}
@Override
public String getLog()
{
return selenium.getLog();
}
@Override
public Number getMouseSpeed()
{
return selenium.getMouseSpeed();
}
@Override
public String getPrompt()
{
return selenium.getPrompt();
}
@Override
public String getSelectedId(String selectLocator)
{
return selenium.getSelectedId(selectLocator);
}
@Override
public String[] getSelectedIds(String selectLocator)
{
return selenium.getSelectedIds(selectLocator);
}
@Override
public String getSelectedIndex(String selectLocator)
{
return selenium.getSelectedIndex(selectLocator);
}
@Override
public String[] getSelectedIndexes(String selectLocator)
{
return selenium.getSelectedIndexes(selectLocator);
}
@Override
public String getSelectedLabel(String selectLocator)
{
return selenium.getSelectedLabel(selectLocator);
}
@Override
public String[] getSelectedLabels(String selectLocator)
{
return selenium.getSelectedLabels(selectLocator);
}
@Override
public String getSelectedValue(String selectLocator)
{
return selenium.getSelectedValue(selectLocator);
}
@Override
public String[] getSelectedValues(String selectLocator)
{
return selenium.getSelectedValues(selectLocator);
}
@Override
public String[] getSelectOptions(String selectLocator)
{
return selenium.getSelectOptions(selectLocator);
}
@Override
public String getSpeed()
{
return selenium.getSpeed();
}
@Override
public String getTable(String tableCellAddress)
{
return selenium.getTable(tableCellAddress);
}
@Override
public String getText(String locator)
{
return selenium.getText(locator);
}
@Override
public String getTitle()
{
return selenium.getTitle();
}
@Override
public String getValue(String locator)
{
return selenium.getValue(locator);
}
@Override
public boolean getWhetherThisFrameMatchFrameExpression(String currentFrameString, String target)
{
return selenium.getWhetherThisFrameMatchFrameExpression(currentFrameString, target);
}
@Override
public boolean getWhetherThisWindowMatchWindowExpression(String currentWindowString, String target)
{
return selenium.getWhetherThisWindowMatchWindowExpression(currentWindowString, target);
}
@Override
public Number getXpathCount(String xpath)
{
return selenium.getXpathCount(xpath);
}
@Override
public void goBack()
{
selenium.goBack();
}
@Override
public void highlight(String locator)
{
selenium.highlight(locator);
}
@Override
public void ignoreAttributesWithoutValue(String ignore)
{
selenium.ignoreAttributesWithoutValue(ignore);
}
@Override
public boolean isAlertPresent()
{
return selenium.isAlertPresent();
}
@Override
public boolean isChecked(String locator)
{
return selenium.isChecked(locator);
}
@Override
public boolean isConfirmationPresent()
{
return selenium.isConfirmationPresent();
}
@Override
public boolean isCookiePresent(String name)
{
return selenium.isCookiePresent(name);
}
@Override
public boolean isEditable(String locator)
{
return selenium.isEditable(locator);
}
@Override
public boolean isElementPresent(String locator)
{
return !webDriver.findElements(convertLocator(locator)).isEmpty();
}
@Override
public boolean isOrdered(String locator1, String locator2)
{
return selenium.isOrdered(locator1, locator2);
}
@Override
public boolean isPromptPresent()
{
return selenium.isPromptPresent();
}
@Override
public boolean isSomethingSelected(String selectLocator)
{
return selenium.isSomethingSelected(selectLocator);
}
@Override
public boolean isTextPresent(String pattern)
{
return selenium.isTextPresent(pattern);
}
@Override
public boolean isVisible(String locator)
{
return selenium.isVisible(locator);
}
@Override
public void keyDown(String locator, String keySequence)
{
selenium.keyDown(locator, keySequence);
}
@Override
public void keyDownNative(String keycode)
{
selenium.keyDownNative(keycode);
}
@Override
public void keyPress(String locator, String keySequence)
{
selenium.keyPress(locator, keySequence);
}
@Override
public void keyPressNative(String keycode)
{
selenium.keyPressNative(keycode);
}
@Override
public void keyUp(String locator, String keySequence)
{
selenium.keyUp(locator, keySequence);
}
@Override
public void keyUpNative(String keycode)
{
selenium.keyUpNative(keycode);
}
@Override
public void metaKeyDown()
{
selenium.metaKeyDown();
}
@Override
public void metaKeyUp()
{
selenium.metaKeyUp();
}
@Override
public void mouseDown(String locator)
{
selenium.mouseDown(locator);
}
@Override
public void mouseDownAt(String locator, String coordString)
{
selenium.mouseDownAt(locator, coordString);
}
@Override
public void mouseDownRight(String locator)
{
selenium.mouseDownRight(locator);
}
@Override
public void mouseDownRightAt(String locator, String coordString)
{
selenium.mouseDownRightAt(locator, coordString);
}
@Override
public void mouseMove(String locator)
{
selenium.mouseMove(locator);
}
@Override
public void mouseMoveAt(String locator, String coordString)
{
selenium.mouseMoveAt(locator, coordString);
}
@Override
public void mouseOut(String locator)
{
selenium.mouseOut(locator);
}
@Override
public void mouseOver(String locator)
{
selenium.mouseOver(locator);
}
@Override
public void mouseUp(String locator)
{
selenium.mouseUp(locator);
}
@Override
public void mouseUpAt(String locator, String coordString)
{
selenium.mouseUpAt(locator, coordString);
}
@Override
public void mouseUpRight(String locator)
{
selenium.mouseUpRight(locator);
}
@Override
public void mouseUpRightAt(String locator, String coordString)
{
selenium.mouseUpRightAt(locator, coordString);
}
@Override
public void open(String url)
{
selenium.open(url);
}
@Override
public void open(String url, String ignoreResponseCode)
{
selenium.open(url, ignoreResponseCode);
}
@Override
public void openWindow(String url, String windowID)
{
selenium.openWindow(url, windowID);
}
@Override
public void refresh()
{
selenium.refresh();
}
@Override
public void removeAllSelections(String locator)
{
selenium.removeAllSelections(locator);
}
@Override
public void removeScript(String scriptTagId)
{
selenium.removeScript(scriptTagId);
}
@Override
public void removeSelection(String locator, String optionLocator)
{
selenium.removeSelection(locator, optionLocator);
}
@Override
public String retrieveLastRemoteControlLogs()
{
return selenium.retrieveLastRemoteControlLogs();
}
@Override
public void rollup(String rollupName, String kwargs)
{
selenium.rollup(rollupName, kwargs);
}
@Override
public void runScript(String script)
{
selenium.runScript(script);
}
@Override
public void select(String selectLocator, String optionLocator)
{
selenium.select(selectLocator, optionLocator);
}
@Override
public void selectFrame(String locator)
{
selenium.selectFrame(locator);
}
@Override
public void selectPopUp(String windowID)
{
selenium.selectPopUp(windowID);
}
@Override
public void selectWindow(String windowID)
{
selenium.selectWindow(windowID);
}
@Override
public void setBrowserLogLevel(String logLevel)
{
selenium.setBrowserLogLevel(logLevel);
}
@Override
public void setContext(String context)
{
selenium.setContext(context);
}
@Override
public void setCursorPosition(String locator, String position)
{
selenium.setCursorPosition(locator, position);
}
@Override
public void setExtensionJs(String extensionJs)
{
selenium.setExtensionJs(extensionJs);
}
@Override
public void setMouseSpeed(String pixels)
{
selenium.setMouseSpeed(pixels);
}
@Override
public void setSpeed(String value)
{
selenium.setSpeed(value);
}
@Override
public void setTimeout(String timeout)
{
selenium.setTimeout(timeout);
}
@Override
public void shiftKeyDown()
{
selenium.shiftKeyDown();
}
@Override
public void shiftKeyUp()
{
selenium.shiftKeyUp();
}
@Override
public void showContextualBanner()
{
selenium.showContextualBanner();
}
@Override
public void showContextualBanner(String className, String methodName)
{
selenium.showContextualBanner(className, methodName);
}
@Override
public void shutDownSeleniumServer()
{
selenium.shutDownSeleniumServer();
}
@Override
public void start()
{
selenium.start();
}
@Override
public void start(Object optionsObject)
{
selenium.start(optionsObject);
}
@Override
public void start(String optionsString)
{
selenium.start(optionsString);
}
@Override
public void stop()
{
selenium.stop();
}
@Override
public void submit(String formLocator)
{
selenium.submit(formLocator);
}
@Override
public void type(String locator, String value)
{
WebElement element = webDriver.findElement(convertLocator(locator));
((JavascriptExecutor) webDriver).executeScript("arguments[0].value = arguments[1];", element, value);
}
@Override
public void typeKeys(String locator, String value)
{
WebElement element = webDriver.findElement(convertLocator(locator));
element.sendKeys(value);
}
@Override
public void uncheck(String locator)
{
selenium.uncheck(locator);
}
@Override
public void useXpathLibrary(String libraryName)
{
selenium.useXpathLibrary(libraryName);
}
@Override
public void waitForCondition(String script, String timeout)
{
selenium.waitForCondition(script, timeout);
}
protected void waitForCondition(ExpectedCondition condition)
{
waitForCondition(condition, 10l);
}
protected void waitForCondition(ExpectedCondition condition, long timeoutSeconds)
{
WebDriverWait wait = new WebDriverWait(webDriver, timeoutSeconds);
wait.until(condition);
}
@Override
public void waitForFrameToLoad(String frameAddress, String timeout)
{
selenium.waitForFrameToLoad(frameAddress, timeout);
}
/**
* Waits for page to load, then waits for initialization to finish, which is recognized by the {@code data-page-initialized} attribute
* being set to true on the body element. Polls at increasing intervals, for up-to 30 seconds (that's extraordinarily long, but helps sometimes
* when manually debugging a page that doesn't have the floating console enabled)..
*/
@Override
public void waitForPageToLoad(String timeout)
{
selenium.waitForPageToLoad(timeout);
// In a limited number of cases, a "page" is an container error page or raw HTML content
// that does not include the body element and data-page-initialized element. In those cases,
// there will never be page initialization in the Tapestry sense and we return immediately.
try
{
WebElement body = webDriver.findElement(By.cssSelector("body"));
if (body.getAttribute("data-page-initialized") == null)
{
return;
}
// Attempt to fix StaleElementReferenceException: The element reference of <body> is stale; either the element is no longer attached to the DOM, it is not in the current frame context, or the document has been refreshed
// waitForCondition(ExpectedConditions.attributeToBe(body, "data-page-initialized", "true"), 30);
waitForCssSelectorToAppear("body[data-page-initialized='true']");
} catch (NoSuchElementException e)
{
// no body element found, there's nothing to wait for
} catch (StaleElementReferenceException e) {
e.printStackTrace();
System.out.println("Continuing execution after exception above.");
}
}
@Override
public void waitForPopUp(String windowID, String timeout)
{
selenium.waitForPopUp(windowID, timeout);
}
@Override
public void windowFocus()
{
selenium.windowFocus();
}
@Override
public void windowMaximize()
{
selenium.windowMaximize();
}
// ---------------------------------------------------------------------
// End of delegate methods
// ---------------------------------------------------------------------
public void scrollIntoView(WebElement element)
{
((JavascriptExecutor) webDriver).executeScript("arguments[0].scrollIntoView(true);", element);
}
/**
* Formats a message from the provided arguments, which is written to System.err. In addition,
* captures the AUT's markup, screenshot, and a report to the output directory.
*
* @param message
* @param arguments
* @since 5.4
*/
protected final void reportAndThrowAssertionError(String message, Object... arguments)
{
StringBuilder builder = new StringBuilder(5000);
String formatted = String.format(message, arguments);
builder.append(formatted);
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
StringBuilder buffer = new StringBuilder(5000);
boolean enabled = false;
for (StackTraceElement e : stackTrace)
{
if (enabled)
{
buffer.append("\n- ");
buffer.append(e);
continue;
}
if (e.getMethodName().equals("reportAndThrowAssertionError"))
{
enabled = true;
}
}
writeErrorReport(builder.toString());
throw new AssertionError(formatted);
}
protected final void unreachable()
{
reportAndThrowAssertionError("An unreachable statement was reached.");
}
/**
* Open the {@linkplain #getBaseURL()}, and waits for the page to load.
*/
protected final void openBaseURL()
{
open(baseURL);
waitForPageToLoad();
}
/**
* Asserts the text of an element, identified by the locator.
*
* @param locator
* identifies the element whose text value is to be asserted
* @param expected
* expected value for the element's text
*/
protected final void assertText(String locator, String expected)
{
String actual = null;
try
{
actual = getText(locator);
} catch (RuntimeException ex)
{
System.err.printf("Error accessing %s: %s, in:\n\n%s\n\n", locator, ex.getMessage(), getHtmlSource());
throw ex;
}
if (actual.equals(expected))
{
return;
}
reportAndThrowAssertionError("%s was '%s' not '%s'", locator, actual, expected);
}
protected final void assertTextPresent(String... text)
{
for (String item : text)
{
if (isTextPresent(item))
{
continue;
}
reportAndThrowAssertionError("Page did not contain '" + item + "'.");
}
}
/**
* Assets that each string provided is present somewhere in the current document.
*
* @param expected
* string expected to be present
*/
protected final void assertSourcePresent(String... expected)
{
String source = getHtmlSource();
for (String snippet : expected)
{
if (source.contains(snippet))
{
continue;
}
reportAndThrowAssertionError("Page did not contain source '" + snippet + "'.");
}
}
/**
* Click a link identified by a locator, then wait for the resulting page to load.
* This is not useful for Ajax updates, just normal full-page refreshes.
*
* @param locator
* identifies the link to click
*/
protected final void clickAndWait(String locator)
{
click(locator);
waitForPageToLoad();
}
/**
* Waits for the page to load (up to 15 seconds). This is invoked after clicking on an element
* that forces a full page refresh.
*/
protected final void waitForPageToLoad()
{
waitForPageToLoad(PAGE_LOAD_TIMEOUT);
}
/**
* Used when the locator identifies an attribute, not an element.
*
* @param locator
* identifies the attribute whose value is to be asserted
* @param expected
* expected value for the attribute
*/
protected final void assertAttribute(String locator, String expected)
{
String actual = null;
try
{
actual = getAttribute(locator);
} catch (RuntimeException ex)
{
reportAndThrowAssertionError("Error accessing %s: %s", locator, ex.getMessage());
}
if (actual.equals(expected))
{
return;
}
reportAndThrowAssertionError("%s was '%s' not '%s'", locator, actual, expected);
}
/**
* Assets that the value in the field matches the expectation
*
* @param locator
* identifies the field
* @param expected
* expected value for the field
* @since 5.3
*/
protected final void assertFieldValue(String locator, String expected)
{
try
{
assertEquals(getValue(locator), expected);
} catch (AssertionError ex)
{
reportAndThrowAssertionError("Failure accessing %s: %s", locator, ex);
}
}
/**
* Opens the base URL, then clicks through a series of links to get to a desired application
* state.
*
* @since 5.3
*/
protected final void openLinks(String... linkText)
{
openBaseURL();
for (String text : linkText)
{
clickAndWait("link=" + text);
}
}
/**
* Sleeps for the indicated number of seconds.
*
* @since 5.3
*/
protected final void sleep(long millis)
{
try
{
Thread.sleep(millis);
} catch (InterruptedException ex)
{
// Ignore.
}
}
/**
* Waits for the element with the given client-side id to be present in the DOM (
* does not assure that the element is visible).
*
* @param elementId
* identifies the element
* @since 5.3
*/
protected final void waitForElementToAppear(String elementId)
{
String condition = String.format("selenium.browserbot.getCurrentWindow().document.getElementById(\"%s\")", elementId);
waitForCondition(condition, PAGE_LOAD_TIMEOUT);
}
/**
* Waits for an element with a given CSS selector to appear.
*
* @param selector
* the CSS selector to wait.
* @since 5.5
*/
protected final void waitForCssSelectorToAppear(String selector)
{
String condition = String.format("selenium.browserbot.getCurrentWindow().document.querySelector(\"%s\")", selector);
waitForCondition(condition, PAGE_LOAD_TIMEOUT);
}
/**
* Waits for the element to be removed from the DOM.
*
*
* This implementation depends on window being extended with testSupport.isNotVisible().
*
* @param elementId
* client-side id of element
* @since 5.3
* @deprecated Deprecated in 5.4 with no replacement
*/
protected final void waitForElementToDisappear(String elementId)
{
String condition = String.format("selenium.browserbot.getCurrentWindow().testSupport.doesNotExist(\"%s\")", elementId);
waitForCondition(condition, PAGE_LOAD_TIMEOUT);
}
/**
* Waits for the element specified by the selector to become visible
* Note that waitForElementToAppear waits for the element to be present in the dom, visible or not. waitForVisible
* waits for an element that already exists in the dom to become visible.
*
* @param selector
* element selector
* @since 5.3
*/
protected final void waitForVisible(String selector)
{
waitForCondition(ExpectedConditions.visibilityOfElementLocated(convertLocator(selector)));
}
/**
* Waits for the element specified by the selector to become invisible
* Note that waitForElementToDisappear waits for the element to be absent from the dom, visible or not. waitForInvisible
* waits for an existing element to become invisible.
*
* @param selector
* element selector
* @since 5.3
*/
protected final void waitForInvisible(String selector)
{
waitForCondition(ExpectedConditions.invisibilityOfElementLocated(convertLocator(selector)));
}
/**
* Asserts that the current page's title matches the expected value.
*
* @param expected
* value for title
* @since 5.3
*/
protected final void assertTitle(String expected)
{
try
{
assertEquals(getTitle(), expected);
} catch (AssertionError ex)
{
reportAndThrowAssertionError("Unexpected title: %s", ex);
throw ex;
}
}
/**
* Waits until all active XHR requests are completed.
*
* @param timeout
* timeout to wait for (no longer used)
* @since 5.3
* @deprecated Deprecated in 5.4 in favor of the version without a timeout
*/
protected final void waitForAjaxRequestsToComplete(String timeout)
{
waitForAjaxRequestsToComplete();
}
/**
* Waits until all active XHR requests (as noted by the t5/core/dom module)
* have completed.
*
* @since 5.4
*/
protected final void waitForAjaxRequestsToComplete()
{
// Ugly but necessary. Give the Ajax operation sufficient time to execute normally, then start
// polling to see if it has complete.
sleep(250);
// The t5/core/dom module tracks how many Ajax requests are active
// and body[data-ajax-active] as appropriate.
for (int i = 0; i < 10; i++)
{
if (i > 0)
{
sleep(100);
}
if (getCssCount("body[data-ajax-active='0']").equals(1))
{
return;
}
}
reportAndThrowAssertionError("Body 'data-ajax-active' attribute never reverted to '0'.");
}
@Override
public Number getCssCount(String str)
{
return selenium.getCssCount(str);
}
protected static By convertLocator(String locator)
{
if (locator.startsWith("link="))
{
return By.linkText(locator.substring(5));
}
else if (locator.startsWith("css="))
{
return By.cssSelector(locator.substring(4));
}
else if (locator.startsWith("xpath="))
{
return By.xpath(locator.substring(6));
}
else if (locator.startsWith("id="))
{
return By.id(locator.substring(3));
}
else if (locator.startsWith("//"))
{
return By.xpath(locator);
}
else
{
return By.id(locator);
}
}
}