package com.vaadin.tests.tb3; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.lang.reflect.Field; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TimeZone; import java.util.logging.Level; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.junit.Assume; import org.junit.Rule; import org.junit.rules.TestName; import org.junit.runner.Description; import org.junit.runner.RunWith; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.interactions.HasInputDevices; import org.openqa.selenium.interactions.Keyboard; import org.openqa.selenium.interactions.Mouse; import org.openqa.selenium.internal.WrapsElement; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.HttpCommandExecutor; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.ExpectedConditions; import com.vaadin.server.LegacyApplication; import com.vaadin.server.UIProvider; import com.vaadin.testbench.ScreenshotOnFailureRule; import com.vaadin.testbench.TestBenchDriverProxy; import com.vaadin.testbench.TestBenchElement; import com.vaadin.testbench.annotations.BrowserConfiguration; import com.vaadin.testbench.elements.CheckBoxElement; import com.vaadin.testbench.elements.LabelElement; import com.vaadin.testbench.elements.TableElement; import com.vaadin.testbench.elements.VerticalLayoutElement; import com.vaadin.testbench.parallel.Browser; import com.vaadin.testbench.parallel.BrowserUtil; import com.vaadin.testbench.parallel.ParallelTest; import com.vaadin.ui.UI; import elemental.json.JsonObject; import elemental.json.impl.JsonUtil; /** * Base class for TestBench 3+ tests. All TB3+ tests in the project should * extend this class. * * Provides: *
* Native events sometimes cause failure in clicking on buttons/checkboxes
* but are possibly needed for some operations.
*
* @return true, to use "native events", false to use generated Javascript
* events
*/
protected boolean useNativeEventsForIE() {
return true;
}
// FIXME: Remove this once TB4 getRemoteControlName works properly
private RemoteWebDriver getRemoteDriver() {
WebDriver d = getDriver();
if (d instanceof TestBenchDriverProxy) {
try {
Field f = TestBenchDriverProxy.class
.getDeclaredField("actualDriver");
f.setAccessible(true);
return (RemoteWebDriver) f.get(d);
} catch (Exception e) {
e.printStackTrace();
}
}
if (d instanceof RemoteWebDriver) {
return (RemoteWebDriver) d;
}
return null;
}
// FIXME: Remove this once TB4 getRemoteControlName works properly
protected String getRemoteControlName() {
try {
RemoteWebDriver d = getRemoteDriver();
if (d == null) {
return null;
}
HttpCommandExecutor ce = (HttpCommandExecutor) d
.getCommandExecutor();
String hostName = ce.getAddressOfRemoteServer().getHost();
int port = ce.getAddressOfRemoteServer().getPort();
HttpHost host = new HttpHost(hostName, port);
try (DefaultHttpClient client = new DefaultHttpClient()) {
URL sessionURL = new URL("http://" + hostName + ":" + port
+ "/grid/api/testsession?session=" + d.getSessionId());
BasicHttpEntityEnclosingRequest r = new BasicHttpEntityEnclosingRequest(
"POST", sessionURL.toExternalForm());
HttpResponse response = client.execute(host, r);
JsonObject object = extractObject(response);
URL myURL = new URL(object.getString("proxyId"));
if ((myURL.getHost() != null) && (myURL.getPort() != -1)) {
return myURL.getHost();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected boolean logContainsText(String string) {
Listtrue
if should click the menu item;
* false
if not
*/
protected void selectMenu(String menuCaption, boolean click) {
WebElement menuElement = getMenuElement(menuCaption);
new Actions(getDriver()).moveToElement(menuElement).perform();
if (click) {
new Actions(getDriver()).click().perform();
}
}
/**
* Finds the menu item from the DOM based on menu item caption.
*
* @param menuCaption
* caption of the menu item
* @return the found menu item
* @throws NoSuchElementException
* if menu item is not found
*/
protected WebElement getMenuElement(String menuCaption)
throws NoSuchElementException {
// Need the parent span to obtain the correct size
return getDriver().findElement(
By.xpath("//span[text() = '" + menuCaption + "']/.."));
}
/**
* Selects a submenu described by a path of menus from the first MenuBar in
* the UI.
*
* @param menuCaptions
* array of menu captions
*/
protected void selectMenuPath(String... menuCaptions) {
selectMenu(menuCaptions[0], true);
// Make sure menu popup is opened.
waitUntil(e -> isElementPresent(By.className("gwt-MenuBarPopup"))
|| isElementPresent(By.className("v-menubar-popup")));
// Move to the menu item opened below the menu bar.
new Actions(getDriver())
.moveByOffset(0,
getMenuElement(menuCaptions[0]).getSize().getHeight())
.perform();
for (int i = 1; i < menuCaptions.length - 1; i++) {
selectMenu(menuCaptions[i]);
new Actions(getDriver()).moveByOffset(
getMenuElement(menuCaptions[i]).getSize().getWidth(), 0)
.build().perform();
}
selectMenu(menuCaptions[menuCaptions.length - 1], true);
}
/**
* Asserts that an element is present
*
* @param by
* the locator for the element
*/
protected void assertElementPresent(By by) {
assertTrue("Element is not present", isElementPresent(by));
}
/**
* Asserts that an element is not present
*
* @param by
* the locator for the element
*/
protected void assertElementNotPresent(By by) {
assertFalse("Element is present", isElementPresent(by));
}
/**
* Asserts that no error notifications are shown. Requires the use of
* "?debug" as exceptions are otherwise not shown as notifications.
*/
protected void assertNoErrorNotifications() {
assertFalse("Error notification with client side exception is shown",
isNotificationPresent("error"));
}
/**
* Asserts that no system notifications are shown.
*/
protected void assertNoSystemNotifications() {
assertFalse("Error notification with system error exception is shown",
isNotificationPresent("system"));
}
/**
* Asserts that a system notification is shown.
*/
protected void assertSystemNotification() {
assertTrue(
"Error notification with system error exception is not shown",
isNotificationPresent("system"));
}
private boolean isNotificationPresent(String type) {
if ("error".equals(type)) {
assertTrue(
"Debug window must be open to be able to see error notifications",
isDebugWindowOpen());
}
return isElementPresent(By.className("v-Notification-" + type));
}
private boolean isDebugWindowOpen() {
return isElementPresent(By.className("v-debugwindow"));
}
protected void assertNoHorizontalScrollbar(WebElement element,
String errorMessage) {
assertHasHorizontalScrollbar(element, errorMessage, false);
}
protected void assertHorizontalScrollbar(WebElement element,
String errorMessage) {
assertHasHorizontalScrollbar(element, errorMessage, true);
}
private void assertHasHorizontalScrollbar(WebElement element,
String errorMessage, boolean expected) {
// IE rounds clientWidth/clientHeight down and scrollHeight/scrollWidth
// up, so using clientWidth/clientHeight will fail if the element height
// is not an integer
int clientWidth = getClientWidth(element);
int scrollWidth = getScrollWidth(element);
boolean hasScrollbar = scrollWidth > clientWidth;
String message = "The element should";
if (!expected) {
message += " not";
}
message += " have a horizontal scrollbar (scrollWidth: " + scrollWidth
+ ", clientWidth: " + clientWidth + "): " + errorMessage;
assertEquals(message, expected, hasScrollbar);
}
protected void assertNoVerticalScrollbar(WebElement element,
String errorMessage) {
// IE rounds clientWidth/clientHeight down and scrollHeight/scrollWidth
// up, so using clientWidth/clientHeight will fail if the element height
// is not an integer
int clientHeight = getClientHeight(element);
int scrollHeight = getScrollHeight(element);
boolean hasScrollbar = scrollHeight > clientHeight;
assertFalse(
"The element should not have a vertical scrollbar (scrollHeight: "
+ scrollHeight + ", clientHeight: " + clientHeight
+ "): " + errorMessage,
hasScrollbar);
}
protected int getScrollHeight(WebElement element) {
return ((Number) executeScript("return arguments[0].scrollHeight;",
element)).intValue();
}
protected int getScrollWidth(WebElement element) {
return ((Number) executeScript("return arguments[0].scrollWidth;",
element)).intValue();
}
protected int getScrollTop(WebElement element) {
return ((Number) executeScript("return arguments[0].scrollTop;",
element)).intValue();
}
/**
* Gets the X offset for
* {@link Actions#moveToElement(WebElement, int, int)}. This method takes
* into account the W3C specification in browsers that properly implement
* it.
*
* @param element
* the element
* @param targetX
* the X coordinate where the move is wanted to go to
* @return the correct X offset
*/
protected int getXOffset(WebElement element, int targetX) {
if (BrowserUtil.isFirefox(getDesiredCapabilities())) {
// Firefox follow W3C spec and moveToElement is relative to center
final int width = element.getSize().getWidth();
return targetX - ((width + width % 2) / 2);
}
if (BrowserUtil.isChrome(getDesiredCapabilities())) {
// Chrome follow W3C spec and moveToElement is relative to center
final int width = element.getSize().getWidth();
return targetX - ((width + width % 2) / 2);
}
return targetX;
}
/**
* Gets the Y offset for
* {@link Actions#moveToElement(WebElement, int, int)}. This method takes
* into account the W3C specification in browsers that properly implement
* it.
*
* @param element
* the element
* @param targetY
* the Y coordinate where the move is wanted to go to
* @return the correct Y offset
*/
protected int getYOffset(WebElement element, int targetY) {
if (BrowserUtil.isFirefox(getDesiredCapabilities())) {
// Firefox follow W3C spec and moveToElement is relative to center
final int height = element.getSize().getHeight();
return targetY - ((height + height % 2) / 2);
}
if (BrowserUtil.isChrome(getDesiredCapabilities())) {
// Chrome follow W3C spec and moveToElement is relative to center
final int height = element.getSize().getHeight();
return targetY - ((height + height % 2) / 2);
}
return targetY;
}
/**
* Returns client height rounded up instead of as double because of IE9
* issues: https://dev.vaadin.com/ticket/18469
*/
protected int getClientHeight(WebElement e) {
String script = "var cs = window.getComputedStyle(arguments[0]);"
+ "return Math.ceil(parseFloat(cs.height)+parseFloat(cs.paddingTop)+parseFloat(cs.paddingBottom));";
return ((Number) executeScript(script, e)).intValue();
}
/**
* Returns client width rounded up instead of as double because of IE9
* issues: https://dev.vaadin.com/ticket/18469
*/
protected int getClientWidth(WebElement e) {
String script = "var cs = window.getComputedStyle(arguments[0]);"
+ "var h = parseFloat(cs.width)+parseFloat(cs.paddingLeft)+parseFloat(cs.paddingRight);"
+ "return Math.ceil(h);";
return ((Number) executeScript(script, e)).intValue();
}
protected TimeZone getBrowserTimeZone() {
Assume.assumeFalse(
"Internet Explorer 11 does not support resolvedOptions timeZone",
BrowserUtil.isIE(getDesiredCapabilities(), 11));
// Ask TimeZone from browser
String browserTimeZone = ((JavascriptExecutor) getDriver())
.executeScript(
"return Intl.DateTimeFormat().resolvedOptions().timeZone;")
.toString();
return TimeZone.getTimeZone(browserTimeZone);
}
protected void assertElementsEquals(WebElement expectedElement,
WebElement actualElement) {
while (expectedElement instanceof WrapsElement) {
expectedElement = ((WrapsElement) expectedElement)
.getWrappedElement();
}
while (actualElement instanceof WrapsElement) {
actualElement = ((WrapsElement) actualElement).getWrappedElement();
}
assertEquals(expectedElement, actualElement);
}
protected WebElement getActiveElement() {
return (WebElement) executeScript("return document.activeElement;");
}
protected void waitForThemeToChange(final String theme) {
final WebElement rootDiv = findElement(
By.xpath("//div[contains(@class,'v-app')]"));
waitUntil(input -> {
String rootClass = rootDiv.getAttribute("class").trim();
return rootClass.contains(theme);
}, 30);
}
}