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 41KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242
  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 static com.vaadin.tests.tb3.TB3Runner.localWebDriverIsUsed;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.StringWriter;
  21. import java.lang.annotation.ElementType;
  22. import java.lang.annotation.Retention;
  23. import java.lang.annotation.RetentionPolicy;
  24. import java.lang.annotation.Target;
  25. import java.lang.reflect.Field;
  26. import java.net.URL;
  27. import java.util.Collections;
  28. import java.util.List;
  29. import org.apache.commons.io.IOUtils;
  30. import org.apache.http.HttpHost;
  31. import org.apache.http.HttpResponse;
  32. import org.apache.http.impl.client.DefaultHttpClient;
  33. import org.apache.http.message.BasicHttpEntityEnclosingRequest;
  34. import org.json.JSONException;
  35. import org.json.JSONObject;
  36. import org.junit.After;
  37. import org.junit.Before;
  38. import org.junit.runner.RunWith;
  39. import org.openqa.selenium.By;
  40. import org.openqa.selenium.JavascriptExecutor;
  41. import org.openqa.selenium.Platform;
  42. import org.openqa.selenium.WebDriver;
  43. import org.openqa.selenium.WebElement;
  44. import org.openqa.selenium.ie.InternetExplorerDriver;
  45. import org.openqa.selenium.interactions.HasInputDevices;
  46. import org.openqa.selenium.interactions.Keyboard;
  47. import org.openqa.selenium.interactions.Mouse;
  48. import org.openqa.selenium.interactions.internal.Coordinates;
  49. import org.openqa.selenium.internal.Locatable;
  50. import org.openqa.selenium.remote.BrowserType;
  51. import org.openqa.selenium.remote.DesiredCapabilities;
  52. import org.openqa.selenium.remote.HttpCommandExecutor;
  53. import org.openqa.selenium.remote.RemoteWebDriver;
  54. import org.openqa.selenium.support.ui.ExpectedCondition;
  55. import org.openqa.selenium.support.ui.ExpectedConditions;
  56. import org.openqa.selenium.support.ui.WebDriverWait;
  57. import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
  58. import com.vaadin.server.LegacyApplication;
  59. import com.vaadin.server.UIProvider;
  60. import com.vaadin.testbench.TestBench;
  61. import com.vaadin.testbench.TestBenchDriverProxy;
  62. import com.vaadin.testbench.TestBenchElement;
  63. import com.vaadin.testbench.TestBenchTestCase;
  64. import com.vaadin.tests.components.AbstractTestUIWithLog;
  65. import com.vaadin.tests.tb3.MultiBrowserTest.Browser;
  66. import com.vaadin.ui.UI;
  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(value = TB3Runner.class)
  84. public abstract class AbstractTB3Test extends TestBenchTestCase {
  85. /**
  86. * Height of the screenshots we want to capture
  87. */
  88. private static final int SCREENSHOT_HEIGHT = 850;
  89. /**
  90. * Width of the screenshots we want to capture
  91. */
  92. private static final int SCREENSHOT_WIDTH = 1500;
  93. /**
  94. * Timeout used by the TB grid
  95. */
  96. private static final int BROWSER_TIMEOUT_IN_MS = 30 * 1000;
  97. private static final int BROWSER_INIT_ATTEMPTS = 5;
  98. private DesiredCapabilities desiredCapabilities;
  99. private boolean debug = false;
  100. private boolean push = false;
  101. {
  102. // Default browser to run on unless setDesiredCapabilities is called
  103. desiredCapabilities = Browser.FIREFOX.getDesiredCapabilities();
  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. @Before
  112. public void setup() throws Exception {
  113. setupDriver();
  114. }
  115. /**
  116. * Creates and configure the web driver to be used for the test. By default
  117. * creates a remote web driver which connects to {@link #getHubURL()} and
  118. * selects a browser based on {@link #getDesiredCapabilities()}.
  119. *
  120. * This method MUST call {@link #setDriver(WebDriver)} with the newly
  121. * generated driver.
  122. *
  123. * @throws Exception
  124. * If something goes wrong
  125. */
  126. protected void setupDriver() throws Exception {
  127. DesiredCapabilities capabilities;
  128. Browser runLocallyBrowser = getRunLocallyBrowser();
  129. if (runLocallyBrowser != null) {
  130. if (System.getenv().containsKey("TEAMCITY_VERSION")) {
  131. throw new RuntimeException(
  132. "@RunLocally is not supported for tests run on the build server");
  133. }
  134. capabilities = runLocallyBrowser.getDesiredCapabilities();
  135. setupLocalDriver(capabilities);
  136. } else {
  137. capabilities = getDesiredCapabilities();
  138. if (localWebDriverIsUsed()) {
  139. setupLocalDriver(capabilities);
  140. } else {
  141. setupRemoteDriver(capabilities);
  142. }
  143. }
  144. int w = SCREENSHOT_WIDTH;
  145. int h = SCREENSHOT_HEIGHT;
  146. if (BrowserUtil.isIE8(capabilities)) {
  147. // IE8 gets size wrong, who would have guessed...
  148. w += 4;
  149. h += 4;
  150. }
  151. try {
  152. testBench().resizeViewPortTo(w, h);
  153. } catch (UnsupportedOperationException e) {
  154. // Opera does not support this...
  155. }
  156. }
  157. protected Browser getRunLocallyBrowser() {
  158. RunLocally runLocally = getClass().getAnnotation(RunLocally.class);
  159. if (runLocally != null) {
  160. return runLocally.value();
  161. } else {
  162. return null;
  163. }
  164. }
  165. protected WebElement getTooltipElement() {
  166. return getDriver().findElement(
  167. com.vaadin.testbench.By.className("v-tooltip-text"));
  168. }
  169. protected Coordinates getCoordinates(TestBenchElement element) {
  170. return ((Locatable) element.getWrappedElement()).getCoordinates();
  171. }
  172. private boolean hasDebugMessage(String message) {
  173. return getDebugMessage(message) != null;
  174. }
  175. private WebElement getDebugMessage(String message) {
  176. return driver.findElement(By.xpath(String.format(
  177. "//span[@class='v-debugwindow-message' and text()='%s']",
  178. message)));
  179. }
  180. protected void waitForDebugMessage(final String expectedMessage) {
  181. waitForDebugMessage(expectedMessage, 30);
  182. }
  183. protected void waitForDebugMessage(final String expectedMessage, int timeout) {
  184. waitUntil(new ExpectedCondition<Boolean>() {
  185. @Override
  186. public Boolean apply(WebDriver input) {
  187. return hasDebugMessage(expectedMessage);
  188. }
  189. }, timeout);
  190. }
  191. protected void clearDebugMessages() {
  192. driver.findElement(
  193. By.xpath("//button[@class='v-debugwindow-button' and @title='Clear log']"))
  194. .click();
  195. }
  196. @Retention(RetentionPolicy.RUNTIME)
  197. @Target(ElementType.TYPE)
  198. public @interface RunLocally {
  199. public Browser value() default Browser.FIREFOX;
  200. }
  201. /**
  202. * Creates a {@link WebDriver} instance used for running the test locally
  203. * for debug purposes. Used only when {@link #runLocally()} is overridden to
  204. * return true;
  205. */
  206. protected abstract void setupLocalDriver(
  207. DesiredCapabilities desiredCapabilities);
  208. /**
  209. * Creates a {@link WebDriver} instance used for running the test remotely.
  210. *
  211. * @since
  212. * @param capabilities
  213. * the type of browser needed
  214. * @throws Exception
  215. */
  216. private void setupRemoteDriver(DesiredCapabilities capabilities)
  217. throws Exception {
  218. if (BrowserUtil.isIE(capabilities)) {
  219. capabilities.setCapability(
  220. InternetExplorerDriver.REQUIRE_WINDOW_FOCUS,
  221. requireWindowFocusForIE());
  222. capabilities.setCapability(
  223. InternetExplorerDriver.ENABLE_PERSISTENT_HOVERING,
  224. usePersistentHoverForIE());
  225. }
  226. for (int i = 1; i <= BROWSER_INIT_ATTEMPTS; i++) {
  227. try {
  228. WebDriver dr = TestBench.createDriver(new RemoteWebDriver(
  229. new URL(getHubURL()), capabilities));
  230. setDriver(dr);
  231. return;
  232. } catch (Exception e) {
  233. System.err.println("Browser startup for " + capabilities
  234. + " failed on attempt " + i + ": " + e.getMessage());
  235. if (i == BROWSER_INIT_ATTEMPTS) {
  236. throw e;
  237. }
  238. }
  239. }
  240. }
  241. /**
  242. * Opens the given test (defined by {@link #getTestUrl()}, optionally with
  243. * debug window and/or push (depending on {@link #isDebug()} and
  244. * {@link #isPush()}.
  245. */
  246. protected void openTestURL() {
  247. openTestURL("");
  248. }
  249. /**
  250. * Opens the given test (defined by {@link #getTestUrl()}, optionally with
  251. * debug window and/or push (depending on {@link #isDebug()} and
  252. * {@link #isPush()}.
  253. */
  254. protected void openTestURL(String extraParameters) {
  255. String url = getTestUrl();
  256. if (url.contains("?")) {
  257. url = url + "&" + extraParameters;
  258. } else {
  259. url = url + "?" + extraParameters;
  260. }
  261. driver.get(url);
  262. }
  263. /**
  264. * Returns the full URL to be used for the test
  265. *
  266. * @return the full URL for the test
  267. */
  268. protected String getTestUrl() {
  269. String baseUrl = getBaseURL();
  270. if (baseUrl.endsWith("/")) {
  271. baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
  272. }
  273. return baseUrl + getDeploymentPath();
  274. }
  275. /**
  276. *
  277. * @return the location (URL) of the TB hub
  278. */
  279. protected String getHubURL() {
  280. return "http://" + getHubHostname() + ":4444/wd/hub";
  281. }
  282. /**
  283. * Used for building the hub URL to use for the test
  284. *
  285. * @return the host name of the TestBench hub
  286. */
  287. protected abstract String getHubHostname();
  288. /**
  289. * Used to determine what URL to initially open for the test
  290. *
  291. * @return the host name of development server
  292. */
  293. protected abstract String getDeploymentHostname();
  294. /**
  295. * Used to determine what port the test is running on
  296. *
  297. * @return The port teh test is running on, by default 8888
  298. */
  299. protected abstract int getDeploymentPort();
  300. /**
  301. * Produces a collection of browsers to run the test on. This method is
  302. * executed by the test runner when determining how many test methods to
  303. * invoke and with what parameters. For each returned value a test method is
  304. * ran and before running that,
  305. * {@link #setDesiredCapabilities(DesiredCapabilities)} is invoked with the
  306. * value returned by this method.
  307. *
  308. * This method is not static to allow overriding it in sub classes. By
  309. * default runs the test only on Firefox
  310. *
  311. * @return The browsers to run the test on
  312. */
  313. public List<DesiredCapabilities> getBrowsersToTest() {
  314. return Collections.singletonList(Browser.FIREFOX
  315. .getDesiredCapabilities());
  316. }
  317. /**
  318. * Used to determine which capabilities should be used when setting up a
  319. * {@link WebDriver} for this test. Typically set by a test runner or left
  320. * at its default (Firefox 24). If you want to run a test on a single
  321. * browser other than Firefox 24 you can override this method.
  322. *
  323. * @return the requested browser capabilities
  324. */
  325. protected DesiredCapabilities getDesiredCapabilities() {
  326. return desiredCapabilities;
  327. }
  328. /**
  329. * Sets the requested browser capabilities (typically browser name and
  330. * version)
  331. *
  332. * @param desiredCapabilities
  333. */
  334. public void setDesiredCapabilities(DesiredCapabilities desiredCapabilities) {
  335. this.desiredCapabilities = desiredCapabilities;
  336. }
  337. /**
  338. * Shuts down the driver after the test has been completed
  339. *
  340. * @throws Exception
  341. */
  342. @After
  343. public void tearDown() throws Exception {
  344. if (driver != null) {
  345. driver.quit();
  346. }
  347. driver = null;
  348. }
  349. /**
  350. * Finds an element based on the part of a TB2 style locator following the
  351. * :: (e.g. vaadin=runLabelModes::PID_Scheckboxaction-Enabled/domChild[0] ->
  352. * PID_Scheckboxaction-Enabled/domChild[0]).
  353. *
  354. * @param vaadinLocator
  355. * The part following :: of the vaadin locator string
  356. * @return
  357. */
  358. protected WebElement vaadinElement(String vaadinLocator) {
  359. return driver.findElement(vaadinLocator(vaadinLocator));
  360. }
  361. /**
  362. * Uses JavaScript to determine the currently focused element.
  363. *
  364. * @return Focused element or null
  365. */
  366. protected WebElement getFocusedElement() {
  367. Object focusedElement = executeScript("return document.activeElement");
  368. if (null != focusedElement) {
  369. return (WebElement) focusedElement;
  370. } else {
  371. return null;
  372. }
  373. }
  374. /**
  375. * Executes the given Javascript
  376. *
  377. * @param script
  378. * the script to execute
  379. * @return whatever
  380. * {@link org.openqa.selenium.JavascriptExecutor#executeScript(String, Object...)}
  381. * returns
  382. */
  383. protected Object executeScript(String script) {
  384. return ((JavascriptExecutor) getDriver()).executeScript(script);
  385. }
  386. /**
  387. * Find a Vaadin element based on its id given using Component.setId
  388. *
  389. * @param id
  390. * The id to locate
  391. * @return
  392. */
  393. public WebElement vaadinElementById(String id) {
  394. return driver.findElement(vaadinLocatorById(id));
  395. }
  396. /**
  397. * Finds a {@link By} locator based on the part of a TB2 style locator
  398. * following the :: (e.g.
  399. * vaadin=runLabelModes::PID_Scheckboxaction-Enabled/domChild[0] ->
  400. * PID_Scheckboxaction-Enabled/domChild[0]).
  401. *
  402. * @param vaadinLocator
  403. * The part following :: of the vaadin locator string
  404. * @return
  405. */
  406. public org.openqa.selenium.By vaadinLocator(String vaadinLocator) {
  407. String base = getApplicationId(getDeploymentPath());
  408. base += "::";
  409. return com.vaadin.testbench.By.vaadin(base + vaadinLocator);
  410. }
  411. /**
  412. * Constructs a {@link By} locator for the id given using Component.setId
  413. *
  414. * @param id
  415. * The id to locate
  416. * @return a locator for the given id
  417. */
  418. public By vaadinLocatorById(String id) {
  419. return vaadinLocator("PID_S" + id);
  420. }
  421. /**
  422. * Waits up to 10s for the given condition to become true. Use e.g. as
  423. * {@link #waitUntil(ExpectedConditions.textToBePresentInElement(by, text))}
  424. *
  425. * @param condition
  426. * the condition to wait for to become true
  427. */
  428. protected <T> void waitUntil(ExpectedCondition<T> condition) {
  429. waitUntil(condition, 10);
  430. }
  431. /**
  432. * Waits the given number of seconds for the given condition to become true.
  433. * Use e.g. as {@link
  434. * #waitUntil(ExpectedConditions.textToBePresentInElement(by, text))}
  435. *
  436. * @param condition
  437. * the condition to wait for to become true
  438. */
  439. protected <T> void waitUntil(ExpectedCondition<T> condition,
  440. long timeoutInSeconds) {
  441. new WebDriverWait(driver, timeoutInSeconds).until(condition);
  442. }
  443. /**
  444. * Waits up to 10s for the given condition to become false. Use e.g. as
  445. * {@link #waitUntilNot(ExpectedConditions.textToBePresentInElement(by,
  446. * text))}
  447. *
  448. * @param condition
  449. * the condition to wait for to become false
  450. */
  451. protected <T> void waitUntilNot(ExpectedCondition<T> condition) {
  452. waitUntilNot(condition, 10);
  453. }
  454. /**
  455. * Waits the given number of seconds for the given condition to become
  456. * false. Use e.g. as {@link
  457. * #waitUntilNot(ExpectedConditions.textToBePresentInElement(by, text))}
  458. *
  459. * @param condition
  460. * the condition to wait for to become false
  461. */
  462. protected <T> void waitUntilNot(ExpectedCondition<T> condition,
  463. long timeoutInSeconds) {
  464. waitUntil(ExpectedConditions.not(condition), timeoutInSeconds);
  465. }
  466. protected void waitForElementPresent(final By by) {
  467. waitUntil(ExpectedConditions.presenceOfElementLocated(by));
  468. }
  469. protected void waitForElementVisible(final By by) {
  470. waitUntil(ExpectedConditions.visibilityOfElementLocated(by));
  471. }
  472. /**
  473. * Checks if the given element has the given class name.
  474. *
  475. * Matches only full class names, i.e. has ("foo") does not match
  476. * class="foobar"
  477. *
  478. * @param element
  479. * @param className
  480. * @return
  481. */
  482. protected boolean hasCssClass(WebElement element, String className) {
  483. String classes = element.getAttribute("class");
  484. if (classes == null || classes.isEmpty()) {
  485. return (className == null || className.isEmpty());
  486. }
  487. for (String cls : classes.split(" ")) {
  488. if (className.equals(cls)) {
  489. return true;
  490. }
  491. }
  492. return false;
  493. }
  494. /**
  495. * For tests extending {@link AbstractTestUIWithLog}, returns the element
  496. * for the Nth log row
  497. *
  498. * @param rowNr
  499. * The log row to retrieve
  500. * @return the Nth log row
  501. */
  502. protected WebElement getLogRowElement(int rowNr) {
  503. return vaadinElementById("Log_row_" + rowNr);
  504. }
  505. /**
  506. * For tests extending {@link AbstractTestUIWithLog}, returns the text in
  507. * the Nth log row
  508. *
  509. * @param rowNr
  510. * The log row to retrieve text for
  511. * @return the text in the log row
  512. */
  513. protected String getLogRow(int rowNr) {
  514. return getLogRowElement(rowNr).getText();
  515. }
  516. /**
  517. * Asserts that {@literal a} is &gt;= {@literal b}
  518. *
  519. * @param message
  520. * The message to include in the {@link AssertionError}
  521. * @param a
  522. * @param b
  523. * @throws AssertionError
  524. * If comparison fails
  525. */
  526. public static final <T> void assertGreaterOrEqual(String message,
  527. Comparable<T> a, T b) throws AssertionError {
  528. if (a.compareTo(b) >= 0) {
  529. return;
  530. }
  531. throw new AssertionError(decorate(message, a, b));
  532. }
  533. /**
  534. * Asserts that {@literal a} is &gt; {@literal b}
  535. *
  536. * @param message
  537. * The message to include in the {@link AssertionError}
  538. * @param a
  539. * @param b
  540. * @throws AssertionError
  541. * If comparison fails
  542. */
  543. public static final <T> void assertGreater(String message, Comparable<T> a,
  544. T b) throws AssertionError {
  545. if (a.compareTo(b) > 0) {
  546. return;
  547. }
  548. throw new AssertionError(decorate(message, a, b));
  549. }
  550. /**
  551. * Asserts that {@literal a} is &lt;= {@literal b}
  552. *
  553. * @param message
  554. * The message to include in the {@link AssertionError}
  555. * @param a
  556. * @param b
  557. * @throws AssertionError
  558. * If comparison fails
  559. */
  560. public static final <T> void assertLessThanOrEqual(String message,
  561. Comparable<T> a, T b) throws AssertionError {
  562. if (a.compareTo(b) <= 0) {
  563. return;
  564. }
  565. throw new AssertionError(decorate(message, a, b));
  566. }
  567. /**
  568. * Asserts that {@literal a} is &lt; {@literal b}
  569. *
  570. * @param message
  571. * The message to include in the {@link AssertionError}
  572. * @param a
  573. * @param b
  574. * @throws AssertionError
  575. * If comparison fails
  576. */
  577. public static final <T> void assertLessThan(String message,
  578. Comparable<T> a, T b) throws AssertionError {
  579. if (a.compareTo(b) < 0) {
  580. return;
  581. }
  582. throw new AssertionError(decorate(message, a, b));
  583. }
  584. private static <T> String decorate(String message, Comparable<T> a, T b) {
  585. message = message.replace("{0}", a.toString());
  586. message = message.replace("{1}", b.toString());
  587. return message;
  588. }
  589. /**
  590. * Returns the path that should be used for the test. The path contains the
  591. * full path (appended to hostname+port) and must start with a slash.
  592. *
  593. * @param push
  594. * true if "?debug" should be added
  595. * @param debug
  596. * true if /run-push should be used instead of /run
  597. *
  598. * @return The URL path to the UI class to test
  599. */
  600. protected String getDeploymentPath() {
  601. Class<?> uiClass = getUIClass();
  602. if (uiClass != null) {
  603. return getDeploymentPath(uiClass);
  604. }
  605. throw new IllegalArgumentException("Unable to determine path for "
  606. + getClass().getCanonicalName());
  607. }
  608. /**
  609. * Returns the UI class the current test is connected to (or in special
  610. * cases UIProvider or LegacyApplication). Uses the enclosing class if the
  611. * test class is a static inner class to a UI class.
  612. *
  613. * Test which are not enclosed by a UI class must implement this method and
  614. * return the UI class they want to test.
  615. *
  616. * Note that this method will update the test name to the enclosing class to
  617. * be compatible with TB2 screenshot naming
  618. *
  619. * @return the UI class the current test is connected to
  620. */
  621. protected Class<?> getUIClass() {
  622. try {
  623. // Convention: SomeUITest uses the SomeUI UI class
  624. String uiClassName = getClass().getName().replaceFirst("Test$", "");
  625. Class<?> cls = Class.forName(uiClassName);
  626. if (isSupportedRunnerClass(cls)) {
  627. return cls;
  628. }
  629. } catch (Exception e) {
  630. }
  631. throw new RuntimeException(
  632. "Could not determine UI class. Ensure the test is named UIClassTest and is in the same package as the UIClass");
  633. }
  634. /**
  635. * @return true if the given class is supported by ApplicationServletRunner
  636. */
  637. @SuppressWarnings("deprecation")
  638. private boolean isSupportedRunnerClass(Class<?> cls) {
  639. if (UI.class.isAssignableFrom(cls)) {
  640. return true;
  641. }
  642. if (UIProvider.class.isAssignableFrom(cls)) {
  643. return true;
  644. }
  645. if (LegacyApplication.class.isAssignableFrom(cls)) {
  646. return true;
  647. }
  648. return false;
  649. }
  650. /**
  651. * Returns whether to run the test in debug mode (with the debug console
  652. * open) or not
  653. *
  654. * @return true to run with the debug window open, false by default
  655. */
  656. protected final boolean isDebug() {
  657. return debug;
  658. }
  659. /**
  660. * Sets whether to run the test in debug mode (with the debug console open)
  661. * or not.
  662. *
  663. * @param debug
  664. * true to open debug window, false otherwise
  665. */
  666. protected final void setDebug(boolean debug) {
  667. this.debug = debug;
  668. }
  669. /**
  670. * Returns whether to run the test with push enabled (using /run-push) or
  671. * not. Note that push tests can and should typically be created using @Push
  672. * on the UI instead of overriding this method
  673. *
  674. * @return true if /run-push is used, false otherwise
  675. */
  676. protected final boolean isPush() {
  677. return push;
  678. }
  679. /**
  680. * Sets whether to run the test with push enabled (using /run-push) or not.
  681. * Note that push tests can and should typically be created using @Push on
  682. * the UI instead of overriding this method
  683. *
  684. * @param push
  685. * true to use /run-push in the test, false otherwise
  686. */
  687. protected final void setPush(boolean push) {
  688. this.push = push;
  689. }
  690. /**
  691. * Returns the path for the given UI class when deployed on the test server.
  692. * The path contains the full path (appended to hostname+port) and must
  693. * start with a slash.
  694. *
  695. * This method takes into account {@link #isPush()} and {@link #isDebug()}
  696. * when the path is generated.
  697. *
  698. * @param uiClass
  699. * @param push
  700. * true if "?debug" should be added
  701. * @param debug
  702. * true if /run-push should be used instead of /run
  703. * @return The path to the given UI class
  704. */
  705. private String getDeploymentPath(Class<?> uiClass) {
  706. String runPath = "/run";
  707. if (isPush()) {
  708. runPath = "/run-push";
  709. }
  710. if (UI.class.isAssignableFrom(uiClass)) {
  711. return runPath + "/" + uiClass.getCanonicalName()
  712. + (isDebug() ? "?debug" : "");
  713. } else if (LegacyApplication.class.isAssignableFrom(uiClass)) {
  714. return runPath + "/" + uiClass.getCanonicalName()
  715. + "?restartApplication" + (isDebug() ? "&debug" : "");
  716. } else {
  717. throw new IllegalArgumentException(
  718. "Unable to determine path for enclosing class "
  719. + uiClass.getCanonicalName());
  720. }
  721. }
  722. /**
  723. * Used to determine what URL to initially open for the test
  724. *
  725. * @return The base URL for the test. Does not include a trailing slash.
  726. */
  727. protected String getBaseURL() {
  728. return "http://" + getDeploymentHostname() + ":" + getDeploymentPort();
  729. }
  730. /**
  731. * Generates the application id based on the URL in a way compatible with
  732. * VaadinServletService.
  733. *
  734. * @param pathWithQueryParameters
  735. * The path part of the URL, possibly still containing query
  736. * parameters
  737. * @return The application ID string used in Vaadin locators
  738. */
  739. private String getApplicationId(String pathWithQueryParameters) {
  740. // Remove any possible URL parameters
  741. String pathWithoutQueryParameters = pathWithQueryParameters.replaceAll(
  742. "\\?.*", "");
  743. if ("".equals(pathWithoutQueryParameters)) {
  744. return "ROOT";
  745. }
  746. // Retain only a-z and numbers
  747. return pathWithoutQueryParameters.replaceAll("[^a-zA-Z0-9]", "");
  748. }
  749. /**
  750. * Sleeps for the given number of ms but ensures that the browser connection
  751. * does not time out.
  752. *
  753. * @param timeoutMillis
  754. * Number of ms to wait
  755. * @throws InterruptedException
  756. */
  757. protected void sleep(int timeoutMillis) throws InterruptedException {
  758. while (timeoutMillis > 0) {
  759. int d = Math.min(BROWSER_TIMEOUT_IN_MS, timeoutMillis);
  760. Thread.sleep(d);
  761. timeoutMillis -= d;
  762. // Do something to keep the connection alive
  763. getDriver().getTitle();
  764. }
  765. }
  766. /**
  767. * Provides helper method for selecting the browser to run on
  768. *
  769. * @author Vaadin Ltd
  770. */
  771. public static class BrowserUtil {
  772. /**
  773. * Gets the capabilities for Safari of the given version
  774. *
  775. * @param version
  776. * the major version
  777. * @return an object describing the capabilities required for running a
  778. * test on the given Safari version
  779. */
  780. public static DesiredCapabilities safari(int version) {
  781. DesiredCapabilities c = DesiredCapabilities.safari();
  782. c.setPlatform(Platform.MAC);
  783. c.setVersion("" + version);
  784. return c;
  785. }
  786. /**
  787. * Gets the capabilities for Chrome of the given version
  788. *
  789. * @param version
  790. * the major version
  791. * @return an object describing the capabilities required for running a
  792. * test on the given Chrome version
  793. */
  794. public static DesiredCapabilities chrome(int version) {
  795. DesiredCapabilities c = DesiredCapabilities.chrome();
  796. c.setVersion("" + version);
  797. c.setPlatform(Platform.XP);
  798. return c;
  799. }
  800. /**
  801. * Gets the capabilities for Opera of the given version
  802. *
  803. * @param version
  804. * the major version
  805. * @return an object describing the capabilities required for running a
  806. * test on the given Opera version
  807. */
  808. public static DesiredCapabilities opera(int version) {
  809. DesiredCapabilities c = DesiredCapabilities.opera();
  810. c.setVersion("" + version);
  811. c.setPlatform(Platform.XP);
  812. return c;
  813. }
  814. /**
  815. * Gets the capabilities for Firefox of the given version
  816. *
  817. * @param version
  818. * the major version
  819. * @return an object describing the capabilities required for running a
  820. * test on the given Firefox version
  821. */
  822. public static DesiredCapabilities firefox(int version) {
  823. DesiredCapabilities c = DesiredCapabilities.firefox();
  824. c.setVersion("" + version);
  825. c.setPlatform(Platform.XP);
  826. return c;
  827. }
  828. /**
  829. * Gets the capabilities for Internet Explorer of the given version
  830. *
  831. * @param version
  832. * the major version
  833. * @return an object describing the capabilities required for running a
  834. * test on the given Internet Explorer version
  835. */
  836. public static DesiredCapabilities ie(int version) {
  837. DesiredCapabilities c = DesiredCapabilities.internetExplorer();
  838. c.setVersion("" + version);
  839. c.setCapability(InternetExplorerDriver.IE_ENSURE_CLEAN_SESSION,
  840. true);
  841. return c;
  842. }
  843. /**
  844. * Gets the capabilities for PhantomJS of the given version
  845. *
  846. * @param version
  847. * the major version
  848. * @return an object describing the capabilities required for running a
  849. * test on the given PhantomJS version
  850. */
  851. public static DesiredCapabilities phantomJS(int version) {
  852. DesiredCapabilities c = DesiredCapabilities.phantomjs();
  853. c.setPlatform(Platform.LINUX);
  854. c.setVersion("" + version);
  855. return c;
  856. }
  857. /**
  858. * Checks if the given capabilities refer to Internet Explorer 8
  859. *
  860. * @param capabilities
  861. * @return true if the capabilities refer to IE8, false otherwise
  862. */
  863. public static boolean isIE8(DesiredCapabilities capabilities) {
  864. return isIE(capabilities) && "8".equals(capabilities.getVersion());
  865. }
  866. /**
  867. * @param capabilities
  868. * The capabilities to check
  869. * @return true if the capabilities refer to Internet Explorer, false
  870. * otherwise
  871. */
  872. public static boolean isIE(DesiredCapabilities capabilities) {
  873. return BrowserType.IE.equals(capabilities.getBrowserName());
  874. }
  875. /**
  876. * @param capabilities
  877. * The capabilities to check
  878. * @return true if the capabilities refer to Chrome, false otherwise
  879. */
  880. public static boolean isChrome(DesiredCapabilities capabilities) {
  881. return BrowserType.CHROME.equals(capabilities.getBrowserName());
  882. }
  883. /**
  884. * @param capabilities
  885. * The capabilities to check
  886. * @return true if the capabilities refer to Opera, false otherwise
  887. */
  888. public static boolean isOpera(DesiredCapabilities capabilities) {
  889. return BrowserType.OPERA.equals(capabilities.getBrowserName());
  890. }
  891. /**
  892. * @param capabilities
  893. * The capabilities to check
  894. * @return true if the capabilities refer to Safari, false otherwise
  895. */
  896. public static boolean isSafari(DesiredCapabilities capabilities) {
  897. return BrowserType.SAFARI.equals(capabilities.getBrowserName());
  898. }
  899. /**
  900. * @param capabilities
  901. * The capabilities to check
  902. * @return true if the capabilities refer to Firefox, false otherwise
  903. */
  904. public static boolean isFirefox(DesiredCapabilities capabilities) {
  905. return BrowserType.FIREFOX.equals(capabilities.getBrowserName());
  906. }
  907. /**
  908. * @param capabilities
  909. * The capabilities to check
  910. * @return true if the capabilities refer to PhantomJS, false otherwise
  911. */
  912. public static boolean isPhantomJS(DesiredCapabilities capabilities) {
  913. return BrowserType.PHANTOMJS.equals(capabilities.getBrowserName());
  914. }
  915. /**
  916. * Returns a human readable identifier of the given browser. Used for
  917. * test naming and screenshots
  918. *
  919. * @param capabilities
  920. * @return a human readable string describing the capabilities
  921. */
  922. public static String getBrowserIdentifier(
  923. DesiredCapabilities capabilities) {
  924. if (isIE(capabilities)) {
  925. return "InternetExplorer";
  926. } else if (isFirefox(capabilities)) {
  927. return "Firefox";
  928. } else if (isChrome(capabilities)) {
  929. return "Chrome";
  930. } else if (isSafari(capabilities)) {
  931. return "Safari";
  932. } else if (isOpera(capabilities)) {
  933. return "Opera";
  934. } else if (isPhantomJS(capabilities)) {
  935. return "PhantomJS";
  936. }
  937. return capabilities.getBrowserName();
  938. }
  939. /**
  940. * Returns a human readable identifier of the platform described by the
  941. * given capabilities. Used mainly for screenshots
  942. *
  943. * @param capabilities
  944. * @return a human readable string describing the platform
  945. */
  946. public static String getPlatform(DesiredCapabilities capabilities) {
  947. if (capabilities.getPlatform() == Platform.WIN8
  948. || capabilities.getPlatform() == Platform.WINDOWS
  949. || capabilities.getPlatform() == Platform.VISTA
  950. || capabilities.getPlatform() == Platform.XP) {
  951. return "Windows";
  952. } else if (capabilities.getPlatform() == Platform.MAC) {
  953. return "Mac";
  954. }
  955. return capabilities.getPlatform().toString();
  956. }
  957. /**
  958. * Returns a string which uniquely (enough) identifies this browser.
  959. * Used mainly in screenshot names.
  960. *
  961. * @param capabilities
  962. *
  963. * @return a unique string for each browser
  964. */
  965. public static String getUniqueIdentifier(
  966. DesiredCapabilities capabilities) {
  967. return getUniqueIdentifier(getPlatform(capabilities),
  968. getBrowserIdentifier(capabilities),
  969. capabilities.getVersion());
  970. }
  971. /**
  972. * Returns a string which uniquely (enough) identifies this browser.
  973. * Used mainly in screenshot names.
  974. *
  975. * @param capabilities
  976. *
  977. * @return a unique string for each browser
  978. */
  979. public static String getUniqueIdentifier(
  980. DesiredCapabilities capabilities, String versionOverride) {
  981. return getUniqueIdentifier(getPlatform(capabilities),
  982. getBrowserIdentifier(capabilities), versionOverride);
  983. }
  984. private static String getUniqueIdentifier(String platform,
  985. String browser, String version) {
  986. return platform + "_" + browser + "_" + version;
  987. }
  988. }
  989. /**
  990. * Called by the test runner whenever there is an exception in the test that
  991. * will cause termination of the test
  992. *
  993. * @param t
  994. * the throwable which caused the termination
  995. */
  996. public void onUncaughtException(Throwable t) {
  997. // Do nothing by default
  998. }
  999. /**
  1000. * Returns the mouse object for doing mouse commands
  1001. *
  1002. * @return Returns the mouse
  1003. */
  1004. public Mouse getMouse() {
  1005. return ((HasInputDevices) getDriver()).getMouse();
  1006. }
  1007. /**
  1008. * Returns the keyboard object for controlling keyboard events
  1009. *
  1010. * @return Return the keyboard
  1011. */
  1012. public Keyboard getKeyboard() {
  1013. return ((HasInputDevices) getDriver()).getKeyboard();
  1014. }
  1015. public void hitButton(String id) {
  1016. if (BrowserUtil.isPhantomJS(getDesiredCapabilities())) {
  1017. driver.findElement(By.id(id)).click();
  1018. } else {
  1019. WebDriverBackedSelenium selenium = new WebDriverBackedSelenium(
  1020. driver, driver.getCurrentUrl());
  1021. selenium.keyPress("id=" + id, "\\13");
  1022. }
  1023. }
  1024. protected void openDebugLogTab() {
  1025. waitUntil(new ExpectedCondition<Boolean>() {
  1026. @Override
  1027. public Boolean apply(WebDriver input) {
  1028. WebElement element = getDebugLogButton();
  1029. return element != null;
  1030. }
  1031. }, 15);
  1032. getDebugLogButton().click();
  1033. }
  1034. private WebElement getDebugLogButton() {
  1035. return findElement(By.xpath("//button[@title='Debug message log']"));
  1036. }
  1037. /**
  1038. * Should the "require window focus" be enabled for Internet Explorer.
  1039. * RequireWindowFocus makes tests more stable but seems to be broken with
  1040. * certain commands such as sendKeys. Therefore it is not enabled by default
  1041. * for all tests
  1042. *
  1043. * @return true, to use the "require window focus" feature, false otherwise
  1044. */
  1045. protected boolean requireWindowFocusForIE() {
  1046. return false;
  1047. }
  1048. /**
  1049. * Should the "enable persistent hover" be enabled for Internet Explorer.
  1050. *
  1051. * Persistent hovering causes continuous firing of mouse over events at the
  1052. * last location the mouse cursor has been moved to. This is to avoid
  1053. * problems where the real mouse cursor is inside the browser window and
  1054. * Internet Explorer uses that location for some undefined operation
  1055. * (http://
  1056. * jimevansmusic.blogspot.fi/2012/06/whats-wrong-with-internet-explorer
  1057. * .html)
  1058. *
  1059. * @return true, to use the "persistent hover" feature, false otherwise
  1060. */
  1061. protected boolean usePersistentHoverForIE() {
  1062. return true;
  1063. }
  1064. // FIXME: Remove this once TB4 getRemoteControlName works properly
  1065. private RemoteWebDriver getRemoteDriver() {
  1066. WebDriver d = getDriver();
  1067. if (d instanceof TestBenchDriverProxy) {
  1068. try {
  1069. Field f = TestBenchDriverProxy.class
  1070. .getDeclaredField("actualDriver");
  1071. f.setAccessible(true);
  1072. return (RemoteWebDriver) f.get(d);
  1073. } catch (Exception e) {
  1074. e.printStackTrace();
  1075. }
  1076. }
  1077. if (d instanceof RemoteWebDriver) {
  1078. return (RemoteWebDriver) d;
  1079. }
  1080. return null;
  1081. }
  1082. // FIXME: Remove this once TB4 getRemoteControlName works properly
  1083. protected String getRemoteControlName() {
  1084. try {
  1085. RemoteWebDriver d = getRemoteDriver();
  1086. if (d == null) {
  1087. return null;
  1088. }
  1089. HttpCommandExecutor ce = (HttpCommandExecutor) d
  1090. .getCommandExecutor();
  1091. String hostName = ce.getAddressOfRemoteServer().getHost();
  1092. int port = ce.getAddressOfRemoteServer().getPort();
  1093. HttpHost host = new HttpHost(hostName, port);
  1094. DefaultHttpClient client = new DefaultHttpClient();
  1095. URL sessionURL = new URL("http://" + hostName + ":" + port
  1096. + "/grid/api/testsession?session=" + d.getSessionId());
  1097. BasicHttpEntityEnclosingRequest r = new BasicHttpEntityEnclosingRequest(
  1098. "POST", sessionURL.toExternalForm());
  1099. HttpResponse response = client.execute(host, r);
  1100. JSONObject object = extractObject(response);
  1101. URL myURL = new URL(object.getString("proxyId"));
  1102. if ((myURL.getHost() != null) && (myURL.getPort() != -1)) {
  1103. return myURL.getHost();
  1104. }
  1105. } catch (Exception e) {
  1106. e.printStackTrace();
  1107. }
  1108. return null;
  1109. }
  1110. private static JSONObject extractObject(HttpResponse resp)
  1111. throws IOException, JSONException {
  1112. InputStream contents = resp.getEntity().getContent();
  1113. StringWriter writer = new StringWriter();
  1114. IOUtils.copy(contents, writer, "UTF8");
  1115. JSONObject objToReturn = new JSONObject(writer.toString());
  1116. return objToReturn;
  1117. }
  1118. }