You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractTB3Test.java 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.tests.tb3;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.io.StringWriter;
  20. import java.lang.reflect.Field;
  21. import java.net.URL;
  22. import java.util.ArrayList;
  23. import java.util.Collections;
  24. import java.util.List;
  25. import java.util.NoSuchElementException;
  26. import org.apache.commons.io.IOUtils;
  27. import org.apache.commons.lang3.StringUtils;
  28. import org.apache.http.HttpHost;
  29. import org.apache.http.HttpResponse;
  30. import org.apache.http.impl.client.DefaultHttpClient;
  31. import org.apache.http.message.BasicHttpEntityEnclosingRequest;
  32. import org.junit.Rule;
  33. import org.junit.runner.RunWith;
  34. import org.openqa.selenium.By;
  35. import org.openqa.selenium.JavascriptExecutor;
  36. import org.openqa.selenium.WebDriver;
  37. import org.openqa.selenium.WebElement;
  38. import org.openqa.selenium.interactions.HasInputDevices;
  39. import org.openqa.selenium.interactions.Keyboard;
  40. import org.openqa.selenium.interactions.Mouse;
  41. import org.openqa.selenium.interactions.internal.Coordinates;
  42. import org.openqa.selenium.internal.Locatable;
  43. import org.openqa.selenium.remote.DesiredCapabilities;
  44. import org.openqa.selenium.remote.HttpCommandExecutor;
  45. import org.openqa.selenium.remote.RemoteWebDriver;
  46. import org.openqa.selenium.support.ui.ExpectedCondition;
  47. import org.openqa.selenium.support.ui.ExpectedConditions;
  48. import org.openqa.selenium.support.ui.WebDriverWait;
  49. import com.google.gwt.thirdparty.guava.common.base.Joiner;
  50. import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
  51. import com.vaadin.server.LegacyApplication;
  52. import com.vaadin.server.UIProvider;
  53. import com.vaadin.testbench.TestBenchDriverProxy;
  54. import com.vaadin.testbench.TestBenchElement;
  55. import com.vaadin.testbench.annotations.BrowserConfiguration;
  56. import com.vaadin.testbench.elements.CheckBoxElement;
  57. import com.vaadin.testbench.elements.LabelElement;
  58. import com.vaadin.testbench.elements.TableElement;
  59. import com.vaadin.testbench.elements.VerticalLayoutElement;
  60. import com.vaadin.testbench.parallel.Browser;
  61. import com.vaadin.testbench.parallel.BrowserUtil;
  62. import com.vaadin.testbench.parallel.ParallelTest;
  63. import com.vaadin.tests.components.AbstractTestUIWithLog;
  64. import com.vaadin.ui.UI;
  65. import elemental.json.JsonObject;
  66. import elemental.json.impl.JsonUtil;
  67. /**
  68. * Base class for TestBench 3+ tests. All TB3+ tests in the project should
  69. * extend this class.
  70. *
  71. * Provides:
  72. * <ul>
  73. * <li>Helpers for browser selection</li>
  74. * <li>Hub connection setup and teardown</li>
  75. * <li>Automatic generation of URL for a given test on the development server
  76. * using {@link #getUIClass()} or by automatically finding an enclosing UI class
  77. * and based on requested features, e.g. {@link #isDebug()}, {@link #isPush()}</li>
  78. * <li>Generic helpers for creating TB3+ tests</li>
  79. * </ul>
  80. *
  81. * @author Vaadin Ltd
  82. */
  83. @RunWith(TB3Runner.class)
  84. public abstract class AbstractTB3Test extends ParallelTest {
  85. @Rule
  86. public RetryOnFail retry = new RetryOnFail();
  87. /**
  88. * Height of the screenshots we want to capture
  89. */
  90. private static final int SCREENSHOT_HEIGHT = 850;
  91. /**
  92. * Width of the screenshots we want to capture
  93. */
  94. private static final int SCREENSHOT_WIDTH = 1500;
  95. /**
  96. * Timeout used by the TB grid
  97. */
  98. private static final int BROWSER_TIMEOUT_IN_MS = 30 * 1000;
  99. private boolean debug = false;
  100. private boolean push = false;
  101. static {
  102. com.vaadin.testbench.Parameters
  103. .setScreenshotComparisonCursorDetection(true);
  104. }
  105. /**
  106. * Connect to the hub using a remote web driver, set the canvas size and
  107. * opens the initial URL as specified by {@link #getTestUrl()}
  108. *
  109. * @throws Exception
  110. */
  111. @Override
  112. public void setup() throws Exception {
  113. super.setup();
  114. int w = SCREENSHOT_WIDTH;
  115. int h = SCREENSHOT_HEIGHT;
  116. if (BrowserUtil.isIE8(super.getDesiredCapabilities())) {
  117. // IE8 gets size wrong, who would have guessed...
  118. w += 4;
  119. h += 4;
  120. }
  121. try {
  122. testBench().resizeViewPortTo(w, h);
  123. } catch (UnsupportedOperationException e) {
  124. // Opera does not support this...
  125. }
  126. }
  127. /**
  128. * Method for closing the tested application.
  129. */
  130. protected void closeApplication() {
  131. if (driver != null) {
  132. try {
  133. openTestURL("closeApplication");
  134. } catch (Exception e) {
  135. e.printStackTrace();
  136. }
  137. }
  138. }
  139. protected WebElement getTooltipElement() {
  140. return getDriver().findElement(
  141. com.vaadin.testbench.By.className("v-tooltip-text"));
  142. }
  143. protected Coordinates getCoordinates(TestBenchElement element) {
  144. return ((Locatable) element.getWrappedElement()).getCoordinates();
  145. }
  146. private boolean hasDebugMessage(String message) {
  147. return getDebugMessage(message) != null;
  148. }
  149. private WebElement getDebugMessage(String message) {
  150. return driver.findElement(By.xpath(String.format(
  151. "//span[@class='v-debugwindow-message' and text()='%s']",
  152. message)));
  153. }
  154. protected void waitForDebugMessage(final String expectedMessage) {
  155. waitForDebugMessage(expectedMessage, 30);
  156. }
  157. protected void waitForDebugMessage(final String expectedMessage, int timeout) {
  158. waitUntil(new ExpectedCondition<Boolean>() {
  159. @Override
  160. public Boolean apply(WebDriver input) {
  161. return hasDebugMessage(expectedMessage);
  162. }
  163. }, timeout);
  164. }
  165. protected void clearDebugMessages() {
  166. driver.findElement(
  167. By.xpath("//button[@class='v-debugwindow-button' and @title='Clear log']"))
  168. .click();
  169. }
  170. protected void waitUntilRowIsVisible(final TableElement table, final int row) {
  171. waitUntil(new ExpectedCondition<Object>() {
  172. @Override
  173. public Object apply(WebDriver input) {
  174. try {
  175. return table.getCell(row, 0) != null;
  176. } catch (NoSuchElementException e) {
  177. return false;
  178. }
  179. }
  180. });
  181. }
  182. protected void scrollTable(TableElement table, int rows, int rowToWait) {
  183. testBenchElement(table.findElement(By.className("v-scrollable")))
  184. .scroll(rows * 30);
  185. waitUntilRowIsVisible(table, rowToWait);
  186. }
  187. /**
  188. * Opens the given test (defined by {@link #getTestUrl()}, optionally with
  189. * debug window and/or push (depending on {@link #isDebug()} and
  190. * {@link #isPush()}.
  191. */
  192. protected void openTestURL(String... parameters) {
  193. openTestURL(getUIClass(), parameters);
  194. }
  195. /**
  196. * Opens the given test (defined by {@link #getTestUrl()}, optionally with
  197. * debug window and/or push (depending on {@link #isDebug()} and
  198. * {@link #isPush()}.
  199. */
  200. protected void openTestURL(Class<?> uiClass, String... parameters) {
  201. String url = getTestURL(uiClass);
  202. if (parameters.length > 0) {
  203. url += "?" + Joiner.on("&").join(parameters);
  204. }
  205. driver.get(url);
  206. }
  207. /**
  208. * Returns the full URL to be used for the test
  209. *
  210. * @return the full URL for the test
  211. */
  212. protected String getTestUrl() {
  213. return StringUtils.strip(getBaseURL(), "/") + getDeploymentPath();
  214. }
  215. /**
  216. * Returns the full URL to be used for the test for the provided UI class.
  217. *
  218. * @return the full URL for the test
  219. */
  220. protected String getTestURL(Class<?> uiClass) {
  221. return StringUtils.strip(getBaseURL(), "/")
  222. + getDeploymentPath(uiClass);
  223. }
  224. /**
  225. * Used to determine what URL to initially open for the test
  226. *
  227. * @return the host name of development server
  228. */
  229. protected abstract String getDeploymentHostname();
  230. /**
  231. * Used to determine what port the test is running on
  232. *
  233. * @return The port teh test is running on, by default 8888
  234. */
  235. protected abstract int getDeploymentPort();
  236. /**
  237. * Produces a collection of browsers to run the test on. This method is
  238. * executed by the test runner when determining how many test methods to
  239. * invoke and with what parameters. For each returned value a test method is
  240. * ran and before running that,
  241. * {@link #setDesiredCapabilities(DesiredCapabilities)} is invoked with the
  242. * value returned by this method.
  243. *
  244. * This method is not static to allow overriding it in sub classes. By
  245. * default runs the test only on Firefox
  246. *
  247. * @return The browsers to run the test on
  248. */
  249. @BrowserConfiguration
  250. public List<DesiredCapabilities> getBrowsersToTest() {
  251. return Collections.singletonList(Browser.FIREFOX
  252. .getDesiredCapabilities());
  253. }
  254. /**
  255. * Finds an element based on the part of a TB2 style locator following the
  256. * :: (e.g. vaadin=runLabelModes::PID_Scheckboxaction-Enabled/domChild[0] ->
  257. * PID_Scheckboxaction-Enabled/domChild[0]).
  258. *
  259. * @param vaadinLocator
  260. * The part following :: of the vaadin locator string
  261. * @return
  262. */
  263. protected WebElement vaadinElement(String vaadinLocator) {
  264. return driver.findElement(vaadinLocator(vaadinLocator));
  265. }
  266. /**
  267. * Uses JavaScript to determine the currently focused element.
  268. *
  269. * @return Focused element or null
  270. */
  271. protected WebElement getFocusedElement() {
  272. Object focusedElement = executeScript("return document.activeElement");
  273. if (null != focusedElement) {
  274. return (WebElement) focusedElement;
  275. } else {
  276. return null;
  277. }
  278. }
  279. /**
  280. * Executes the given Javascript
  281. *
  282. * @param script
  283. * the script to execute
  284. * @return whatever
  285. * {@link org.openqa.selenium.JavascriptExecutor#executeScript(String, Object...)}
  286. * returns
  287. */
  288. protected Object executeScript(String script) {
  289. return ((JavascriptExecutor) getDriver()).executeScript(script);
  290. }
  291. /**
  292. * Find a Vaadin element based on its id given using Component.setId
  293. *
  294. * @param id
  295. * The id to locate
  296. * @return
  297. */
  298. public WebElement vaadinElementById(String id) {
  299. return driver.findElement(vaadinLocatorById(id));
  300. }
  301. /**
  302. * Finds a {@link By} locator based on the part of a TB2 style locator
  303. * following the :: (e.g.
  304. * vaadin=runLabelModes::PID_Scheckboxaction-Enabled/domChild[0] ->
  305. * PID_Scheckboxaction-Enabled/domChild[0]).
  306. *
  307. * @param vaadinLocator
  308. * The part following :: of the vaadin locator string
  309. * @return
  310. */
  311. public org.openqa.selenium.By vaadinLocator(String vaadinLocator) {
  312. String base = getApplicationId(getDeploymentPath());
  313. base += "::";
  314. return com.vaadin.testbench.By.vaadin(base + vaadinLocator);
  315. }
  316. /**
  317. * Constructs a {@link By} locator for the id given using Component.setId
  318. *
  319. * @param id
  320. * The id to locate
  321. * @return a locator for the given id
  322. */
  323. public By vaadinLocatorById(String id) {
  324. return vaadinLocator("PID_S" + id);
  325. }
  326. /**
  327. * Waits up to 10s for the given condition to become true. Use e.g. as
  328. * {@link #waitUntil(ExpectedConditions.textToBePresentInElement(by, text))}
  329. *
  330. * @param condition
  331. * the condition to wait for to become true
  332. */
  333. protected <T> void waitUntil(ExpectedCondition<T> condition) {
  334. waitUntil(condition, 10);
  335. }
  336. /**
  337. * Waits the given number of seconds for the given condition to become true.
  338. * Use e.g. as {@link
  339. * #waitUntil(ExpectedConditions.textToBePresentInElement(by, text))}
  340. *
  341. * @param condition
  342. * the condition to wait for to become true
  343. */
  344. protected <T> void waitUntil(ExpectedCondition<T> condition,
  345. long timeoutInSeconds) {
  346. new WebDriverWait(driver, timeoutInSeconds).until(condition);
  347. }
  348. /**
  349. * Waits up to 10s for the given condition to become false. Use e.g. as
  350. * {@link #waitUntilNot(ExpectedConditions.textToBePresentInElement(by,
  351. * text))}
  352. *
  353. * @param condition
  354. * the condition to wait for to become false
  355. */
  356. protected <T> void waitUntilNot(ExpectedCondition<T> condition) {
  357. waitUntilNot(condition, 10);
  358. }
  359. /**
  360. * Waits the given number of seconds for the given condition to become
  361. * false. Use e.g. as {@link
  362. * #waitUntilNot(ExpectedConditions.textToBePresentInElement(by, text))}
  363. *
  364. * @param condition
  365. * the condition to wait for to become false
  366. */
  367. protected <T> void waitUntilNot(ExpectedCondition<T> condition,
  368. long timeoutInSeconds) {
  369. waitUntil(ExpectedConditions.not(condition), timeoutInSeconds);
  370. }
  371. protected void waitForElementPresent(final By by) {
  372. waitUntil(ExpectedConditions.presenceOfElementLocated(by));
  373. }
  374. protected void waitForElementVisible(final By by) {
  375. waitUntil(ExpectedConditions.visibilityOfElementLocated(by));
  376. }
  377. /**
  378. * Checks if the given element has the given class name.
  379. *
  380. * Matches only full class names, i.e. has ("foo") does not match
  381. * class="foobar"
  382. *
  383. * @param element
  384. * @param className
  385. * @return
  386. */
  387. protected boolean hasCssClass(WebElement element, String className) {
  388. String classes = element.getAttribute("class");
  389. if (classes == null || classes.isEmpty()) {
  390. return (className == null || className.isEmpty());
  391. }
  392. for (String cls : classes.split(" ")) {
  393. if (className.equals(cls)) {
  394. return true;
  395. }
  396. }
  397. return false;
  398. }
  399. /**
  400. * For tests extending {@link AbstractTestUIWithLog}, returns the element
  401. * for the Nth log row
  402. *
  403. * @param rowNr
  404. * The log row to retrieve
  405. * @return the Nth log row
  406. */
  407. protected WebElement getLogRowElement(int rowNr) {
  408. return vaadinElementById("Log_row_" + rowNr);
  409. }
  410. /**
  411. * For tests extending {@link AbstractTestUIWithLog}, returns the text in
  412. * the Nth log row
  413. *
  414. * @param rowNr
  415. * The log row to retrieve text for
  416. * @return the text in the log row
  417. */
  418. protected String getLogRow(int rowNr) {
  419. return getLogRowElement(rowNr).getText();
  420. }
  421. /**
  422. * Asserts that {@literal a} is &gt;= {@literal b}
  423. *
  424. * @param message
  425. * The message to include in the {@link AssertionError}
  426. * @param a
  427. * @param b
  428. * @throws AssertionError
  429. * If comparison fails
  430. */
  431. public static final <T> void assertGreaterOrEqual(String message,
  432. Comparable<T> a, T b) throws AssertionError {
  433. if (a.compareTo(b) >= 0) {
  434. return;
  435. }
  436. throw new AssertionError(decorate(message, a, b));
  437. }
  438. /**
  439. * Asserts that {@literal a} is &gt; {@literal b}
  440. *
  441. * @param message
  442. * The message to include in the {@link AssertionError}
  443. * @param a
  444. * @param b
  445. * @throws AssertionError
  446. * If comparison fails
  447. */
  448. public static final <T> void assertGreater(String message, Comparable<T> a,
  449. T b) throws AssertionError {
  450. if (a.compareTo(b) > 0) {
  451. return;
  452. }
  453. throw new AssertionError(decorate(message, a, b));
  454. }
  455. /**
  456. * Asserts that {@literal a} is &lt;= {@literal b}
  457. *
  458. * @param message
  459. * The message to include in the {@link AssertionError}
  460. * @param a
  461. * @param b
  462. * @throws AssertionError
  463. * If comparison fails
  464. */
  465. public static final <T> void assertLessThanOrEqual(String message,
  466. Comparable<T> a, T b) throws AssertionError {
  467. if (a.compareTo(b) <= 0) {
  468. return;
  469. }
  470. throw new AssertionError(decorate(message, a, b));
  471. }
  472. /**
  473. * Asserts that {@literal a} is &lt; {@literal b}
  474. *
  475. * @param message
  476. * The message to include in the {@link AssertionError}
  477. * @param a
  478. * @param b
  479. * @throws AssertionError
  480. * If comparison fails
  481. */
  482. public static final <T> void assertLessThan(String message,
  483. Comparable<T> a, T b) throws AssertionError {
  484. if (a.compareTo(b) < 0) {
  485. return;
  486. }
  487. throw new AssertionError(decorate(message, a, b));
  488. }
  489. private static <T> String decorate(String message, Comparable<T> a, T b) {
  490. message = message.replace("{0}", a.toString());
  491. message = message.replace("{1}", b.toString());
  492. return message;
  493. }
  494. /**
  495. * Returns the path that should be used for the test. The path contains the
  496. * full path (appended to hostname+port) and must start with a slash.
  497. *
  498. * @param push
  499. * true if "?debug" should be added
  500. * @param debug
  501. * true if /run-push should be used instead of /run
  502. *
  503. * @return The URL path to the UI class to test
  504. */
  505. protected String getDeploymentPath() {
  506. Class<?> uiClass = getUIClass();
  507. if (uiClass != null) {
  508. return getDeploymentPath(uiClass);
  509. }
  510. throw new IllegalArgumentException("Unable to determine path for "
  511. + getClass().getCanonicalName());
  512. }
  513. /**
  514. * Returns the UI class the current test is connected to (or in special
  515. * cases UIProvider or LegacyApplication). Uses the enclosing class if the
  516. * test class is a static inner class to a UI class.
  517. *
  518. * Test which are not enclosed by a UI class must implement this method and
  519. * return the UI class they want to test.
  520. *
  521. * Note that this method will update the test name to the enclosing class to
  522. * be compatible with TB2 screenshot naming
  523. *
  524. * @return the UI class the current test is connected to
  525. */
  526. protected Class<?> getUIClass() {
  527. try {
  528. // Convention: SomeUITest uses the SomeUI UI class
  529. String uiClassName = getClass().getName().replaceFirst("Test$", "");
  530. Class<?> cls = Class.forName(uiClassName);
  531. if (isSupportedRunnerClass(cls)) {
  532. return cls;
  533. }
  534. } catch (Exception e) {
  535. }
  536. throw new RuntimeException(
  537. "Could not determine UI class. Ensure the test is named UIClassTest and is in the same package as the UIClass");
  538. }
  539. /**
  540. * @return true if the given class is supported by ApplicationServletRunner
  541. */
  542. @SuppressWarnings("deprecation")
  543. private boolean isSupportedRunnerClass(Class<?> cls) {
  544. if (UI.class.isAssignableFrom(cls)) {
  545. return true;
  546. }
  547. if (UIProvider.class.isAssignableFrom(cls)) {
  548. return true;
  549. }
  550. if (LegacyApplication.class.isAssignableFrom(cls)) {
  551. return true;
  552. }
  553. return false;
  554. }
  555. /**
  556. * Returns whether to run the test in debug mode (with the debug console
  557. * open) or not
  558. *
  559. * @return true to run with the debug window open, false by default
  560. */
  561. protected final boolean isDebug() {
  562. return debug;
  563. }
  564. /**
  565. * Sets whether to run the test in debug mode (with the debug console open)
  566. * or not.
  567. *
  568. * @param debug
  569. * true to open debug window, false otherwise
  570. */
  571. protected final void setDebug(boolean debug) {
  572. this.debug = debug;
  573. }
  574. /**
  575. * Returns whether to run the test with push enabled (using /run-push) or
  576. * not. Note that push tests can and should typically be created using @Push
  577. * on the UI instead of overriding this method
  578. *
  579. * @return true if /run-push is used, false otherwise
  580. */
  581. protected final boolean isPush() {
  582. return push;
  583. }
  584. /**
  585. * Sets whether to run the test with push enabled (using /run-push) or not.
  586. * Note that push tests can and should typically be created using @Push on
  587. * the UI instead of overriding this method
  588. *
  589. * @param push
  590. * true to use /run-push in the test, false otherwise
  591. */
  592. protected final void setPush(boolean push) {
  593. this.push = push;
  594. }
  595. /**
  596. * Returns the path for the given UI class when deployed on the test server.
  597. * The path contains the full path (appended to hostname+port) and must
  598. * start with a slash.
  599. *
  600. * This method takes into account {@link #isPush()} and {@link #isDebug()}
  601. * when the path is generated.
  602. *
  603. * @param uiClass
  604. * @param push
  605. * true if "?debug" should be added
  606. * @param debug
  607. * true if /run-push should be used instead of /run
  608. * @return The path to the given UI class
  609. */
  610. protected String getDeploymentPath(Class<?> uiClass) {
  611. String runPath = "/run";
  612. if (isPush()) {
  613. runPath = "/run-push";
  614. }
  615. if (UI.class.isAssignableFrom(uiClass)
  616. || UIProvider.class.isAssignableFrom(uiClass)) {
  617. return runPath + "/" + uiClass.getCanonicalName()
  618. + (isDebug() ? "?debug" : "");
  619. } else if (LegacyApplication.class.isAssignableFrom(uiClass)) {
  620. return runPath + "/" + uiClass.getCanonicalName()
  621. + "?restartApplication" + (isDebug() ? "&debug" : "");
  622. } else {
  623. throw new IllegalArgumentException(
  624. "Unable to determine path for enclosing class "
  625. + uiClass.getCanonicalName());
  626. }
  627. }
  628. /**
  629. * Used to determine what URL to initially open for the test
  630. *
  631. * @return The base URL for the test. Does not include a trailing slash.
  632. */
  633. protected String getBaseURL() {
  634. return "http://" + getDeploymentHostname() + ":" + getDeploymentPort();
  635. }
  636. /**
  637. * Generates the application id based on the URL in a way compatible with
  638. * VaadinServletService.
  639. *
  640. * @param pathWithQueryParameters
  641. * The path part of the URL, possibly still containing query
  642. * parameters
  643. * @return The application ID string used in Vaadin locators
  644. */
  645. private String getApplicationId(String pathWithQueryParameters) {
  646. // Remove any possible URL parameters
  647. String pathWithoutQueryParameters = pathWithQueryParameters.replaceAll(
  648. "\\?.*", "");
  649. if ("".equals(pathWithoutQueryParameters)) {
  650. return "ROOT";
  651. }
  652. // Retain only a-z and numbers
  653. return pathWithoutQueryParameters.replaceAll("[^a-zA-Z0-9]", "");
  654. }
  655. /**
  656. * Sleeps for the given number of ms but ensures that the browser connection
  657. * does not time out.
  658. *
  659. * @param timeoutMillis
  660. * Number of ms to wait
  661. * @throws InterruptedException
  662. */
  663. protected void sleep(int timeoutMillis) throws InterruptedException {
  664. while (timeoutMillis > 0) {
  665. int d = Math.min(BROWSER_TIMEOUT_IN_MS, timeoutMillis);
  666. Thread.sleep(d);
  667. timeoutMillis -= d;
  668. // Do something to keep the connection alive
  669. getDriver().getTitle();
  670. }
  671. }
  672. /**
  673. * Called by the test runner whenever there is an exception in the test that
  674. * will cause termination of the test
  675. *
  676. * @param t
  677. * the throwable which caused the termination
  678. */
  679. public void onUncaughtException(Throwable t) {
  680. // Do nothing by default
  681. }
  682. /**
  683. * Returns the mouse object for doing mouse commands
  684. *
  685. * @return Returns the mouse
  686. */
  687. public Mouse getMouse() {
  688. return ((HasInputDevices) getDriver()).getMouse();
  689. }
  690. /**
  691. * Returns the keyboard object for controlling keyboard events
  692. *
  693. * @return Return the keyboard
  694. */
  695. public Keyboard getKeyboard() {
  696. return ((HasInputDevices) getDriver()).getKeyboard();
  697. }
  698. public void hitButton(String id) {
  699. if (BrowserUtil.isPhantomJS(getDesiredCapabilities())) {
  700. driver.findElement(By.id(id)).click();
  701. } else {
  702. WebDriverBackedSelenium selenium = new WebDriverBackedSelenium(
  703. driver, driver.getCurrentUrl());
  704. selenium.keyPress("id=" + id, "\\13");
  705. }
  706. }
  707. protected void openDebugLogTab() {
  708. waitUntil(new ExpectedCondition<Boolean>() {
  709. @Override
  710. public Boolean apply(WebDriver input) {
  711. WebElement element = getDebugLogButton();
  712. return element != null;
  713. }
  714. }, 15);
  715. getDebugLogButton().click();
  716. }
  717. private WebElement getDebugLogButton() {
  718. return findElement(By.xpath("//button[@title='Debug message log']"));
  719. }
  720. /**
  721. * Should the "require window focus" be enabled for Internet Explorer.
  722. * RequireWindowFocus makes tests more stable but seems to be broken with
  723. * certain commands such as sendKeys. Therefore it is not enabled by default
  724. * for all tests
  725. *
  726. * @return true, to use the "require window focus" feature, false otherwise
  727. */
  728. protected boolean requireWindowFocusForIE() {
  729. return false;
  730. }
  731. /**
  732. * Should the "enable persistent hover" be enabled for Internet Explorer.
  733. *
  734. * Persistent hovering causes continuous firing of mouse over events at the
  735. * last location the mouse cursor has been moved to. This is to avoid
  736. * problems where the real mouse cursor is inside the browser window and
  737. * Internet Explorer uses that location for some undefined operation
  738. * (http://
  739. * jimevansmusic.blogspot.fi/2012/06/whats-wrong-with-internet-explorer
  740. * .html)
  741. *
  742. * @return true, to use the "persistent hover" feature, false otherwise
  743. */
  744. protected boolean usePersistentHoverForIE() {
  745. return true;
  746. }
  747. // FIXME: Remove this once TB4 getRemoteControlName works properly
  748. private RemoteWebDriver getRemoteDriver() {
  749. WebDriver d = getDriver();
  750. if (d instanceof TestBenchDriverProxy) {
  751. try {
  752. Field f = TestBenchDriverProxy.class
  753. .getDeclaredField("actualDriver");
  754. f.setAccessible(true);
  755. return (RemoteWebDriver) f.get(d);
  756. } catch (Exception e) {
  757. e.printStackTrace();
  758. }
  759. }
  760. if (d instanceof RemoteWebDriver) {
  761. return (RemoteWebDriver) d;
  762. }
  763. return null;
  764. }
  765. // FIXME: Remove this once TB4 getRemoteControlName works properly
  766. protected String getRemoteControlName() {
  767. try {
  768. RemoteWebDriver d = getRemoteDriver();
  769. if (d == null) {
  770. return null;
  771. }
  772. HttpCommandExecutor ce = (HttpCommandExecutor) d
  773. .getCommandExecutor();
  774. String hostName = ce.getAddressOfRemoteServer().getHost();
  775. int port = ce.getAddressOfRemoteServer().getPort();
  776. HttpHost host = new HttpHost(hostName, port);
  777. DefaultHttpClient client = new DefaultHttpClient();
  778. URL sessionURL = new URL("http://" + hostName + ":" + port
  779. + "/grid/api/testsession?session=" + d.getSessionId());
  780. BasicHttpEntityEnclosingRequest r = new BasicHttpEntityEnclosingRequest(
  781. "POST", sessionURL.toExternalForm());
  782. HttpResponse response = client.execute(host, r);
  783. JsonObject object = extractObject(response);
  784. URL myURL = new URL(object.getString("proxyId"));
  785. if ((myURL.getHost() != null) && (myURL.getPort() != -1)) {
  786. return myURL.getHost();
  787. }
  788. } catch (Exception e) {
  789. e.printStackTrace();
  790. }
  791. return null;
  792. }
  793. protected boolean logContainsText(String string) {
  794. List<String> logs = getLogs();
  795. for (String text : logs) {
  796. if (text.contains(string)) {
  797. return true;
  798. }
  799. }
  800. return false;
  801. }
  802. protected List<String> getLogs() {
  803. VerticalLayoutElement log = $(VerticalLayoutElement.class).id("Log");
  804. List<LabelElement> logLabels = log.$(LabelElement.class).all();
  805. List<String> logTexts = new ArrayList<String>();
  806. for (LabelElement label : logLabels) {
  807. logTexts.add(label.getText());
  808. }
  809. return logTexts;
  810. }
  811. private static JsonObject extractObject(HttpResponse resp)
  812. throws IOException {
  813. InputStream contents = resp.getEntity().getContent();
  814. StringWriter writer = new StringWriter();
  815. IOUtils.copy(contents, writer, "UTF8");
  816. return JsonUtil.parse(writer.toString());
  817. }
  818. protected void click(CheckBoxElement checkbox) {
  819. checkbox.findElement(By.xpath("input")).click();
  820. }
  821. }