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

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