Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

AbstractTB3Test.java 33KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028
  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.lang.annotation.ElementType;
  18. import java.lang.annotation.Retention;
  19. import java.lang.annotation.RetentionPolicy;
  20. import java.lang.annotation.Target;
  21. import java.net.URL;
  22. import java.util.Collections;
  23. import java.util.List;
  24. import com.vaadin.testbench.TestBenchElement;
  25. import org.junit.After;
  26. import org.junit.Before;
  27. import org.junit.runner.RunWith;
  28. import org.openqa.selenium.*;
  29. import org.openqa.selenium.interactions.HasInputDevices;
  30. import org.openqa.selenium.interactions.Keyboard;
  31. import org.openqa.selenium.interactions.Mouse;
  32. import org.openqa.selenium.interactions.internal.Coordinates;
  33. import org.openqa.selenium.internal.Locatable;
  34. import org.openqa.selenium.remote.BrowserType;
  35. import org.openqa.selenium.remote.DesiredCapabilities;
  36. import org.openqa.selenium.remote.RemoteWebDriver;
  37. import org.openqa.selenium.support.ui.ExpectedCondition;
  38. import org.openqa.selenium.support.ui.ExpectedConditions;
  39. import org.openqa.selenium.support.ui.WebDriverWait;
  40. import com.thoughtworks.selenium.webdriven.WebDriverBackedSelenium;
  41. import com.vaadin.server.LegacyApplication;
  42. import com.vaadin.server.UIProvider;
  43. import com.vaadin.testbench.TestBench;
  44. import com.vaadin.testbench.TestBenchTestCase;
  45. import com.vaadin.tests.components.AbstractTestUIWithLog;
  46. import com.vaadin.tests.tb3.MultiBrowserTest.Browser;
  47. import com.vaadin.ui.UI;
  48. import static com.vaadin.tests.tb3.TB3Runner.localWebDriverIsUsed;
  49. /**
  50. * Base class for TestBench 3+ tests. All TB3+ tests in the project should
  51. * extend this class.
  52. *
  53. * Provides:
  54. * <ul>
  55. * <li>Helpers for browser selection</li>
  56. * <li>Hub connection setup and teardown</li>
  57. * <li>Automatic generation of URL for a given test on the development server
  58. * using {@link #getUIClass()} or by automatically finding an enclosing UI class
  59. * and based on requested features, e.g. {@link #isDebug()}, {@link #isPush()}</li>
  60. * <li>Generic helpers for creating TB3+ tests</li>
  61. * </ul>
  62. *
  63. * @author Vaadin Ltd
  64. */
  65. @RunWith(value = TB3Runner.class)
  66. public abstract class AbstractTB3Test extends TestBenchTestCase {
  67. /**
  68. * Height of the screenshots we want to capture
  69. */
  70. private static final int SCREENSHOT_HEIGHT = 850;
  71. /**
  72. * Width of the screenshots we want to capture
  73. */
  74. private static final int SCREENSHOT_WIDTH = 1500;
  75. /**
  76. * Timeout used by the TB grid
  77. */
  78. private static final int BROWSER_TIMEOUT_IN_MS = 30 * 1000;
  79. private DesiredCapabilities desiredCapabilities;
  80. private boolean debug = false;
  81. private boolean push = false;
  82. {
  83. // Default browser to run on unless setDesiredCapabilities is called
  84. desiredCapabilities = Browser.FIREFOX.getDesiredCapabilities();
  85. }
  86. /**
  87. * Connect to the hub using a remote web driver, set the canvas size and
  88. * opens the initial URL as specified by {@link #getTestUrl()}
  89. *
  90. * @throws Exception
  91. */
  92. @Before
  93. public void setup() throws Exception {
  94. setupDriver();
  95. }
  96. /**
  97. * Creates and configure the web driver to be used for the test. By default
  98. * creates a remote web driver which connects to {@link #getHubURL()} and
  99. * selects a browser based on {@link #getDesiredCapabilities()}.
  100. *
  101. * This method MUST call {@link #setDriver(WebDriver)} with the newly
  102. * generated driver.
  103. *
  104. * @throws Exception
  105. * If something goes wrong
  106. */
  107. protected void setupDriver() throws Exception {
  108. DesiredCapabilities capabilities;
  109. RunLocally runLocally = getClass().getAnnotation(RunLocally.class);
  110. if (runLocally != null) {
  111. capabilities = runLocally.value().getDesiredCapabilities();
  112. setupLocalDriver(capabilities);
  113. } else {
  114. capabilities = getDesiredCapabilities();
  115. if (localWebDriverIsUsed()) {
  116. setupLocalDriver(capabilities);
  117. } else {
  118. WebDriver dr = TestBench.createDriver(new RemoteWebDriver(
  119. new URL(getHubURL()), capabilities));
  120. setDriver(dr);
  121. }
  122. }
  123. int w = SCREENSHOT_WIDTH;
  124. int h = SCREENSHOT_HEIGHT;
  125. if (BrowserUtil.isIE8(capabilities)) {
  126. // IE8 gets size wrong, who would have guessed...
  127. w += 4;
  128. h += 4;
  129. }
  130. try {
  131. testBench().resizeViewPortTo(w, h);
  132. } catch (UnsupportedOperationException e) {
  133. // Opera does not support this...
  134. }
  135. }
  136. protected WebElement getTooltipElement() {
  137. return getDriver().findElement(com.vaadin.testbench.By.className("v-tooltip-text"));
  138. }
  139. protected Coordinates getCoordinates(TestBenchElement element) {
  140. return ((Locatable) element.getWrappedElement()).getCoordinates();
  141. }
  142. private boolean hasDebugMessage(String message) {
  143. return getDebugMessage(message) != null;
  144. }
  145. private WebElement getDebugMessage(String message) {
  146. return driver.findElement(By.xpath(String.format(
  147. "//span[@class='v-debugwindow-message' and text()='%s']",
  148. message)));
  149. }
  150. protected void waitForDebugMessage(final String expectedMessage) {
  151. waitForDebugMessage(expectedMessage, 30);
  152. }
  153. protected void waitForDebugMessage(final String expectedMessage, int timeout) {
  154. waitUntil(new ExpectedCondition<Boolean>() {
  155. @Override
  156. public Boolean apply(WebDriver input) {
  157. return hasDebugMessage(expectedMessage);
  158. }
  159. }, timeout);
  160. }
  161. protected void clearDebugMessages() {
  162. driver.findElement(
  163. By.xpath("//button[@class='v-debugwindow-button' and @title='Clear log']"))
  164. .click();
  165. }
  166. @Retention(RetentionPolicy.RUNTIME)
  167. @Target(ElementType.TYPE)
  168. public @interface RunLocally {
  169. public Browser value() default Browser.FIREFOX;
  170. }
  171. /**
  172. * Creates a {@link WebDriver} instance used for running the test locally
  173. * for debug purposes. Used only when {@link #runLocally()} is overridden to
  174. * return true;
  175. */
  176. protected abstract void setupLocalDriver(
  177. DesiredCapabilities desiredCapabilities);
  178. /**
  179. * Opens the given test (defined by {@link #getTestUrl()}, optionally with
  180. * debug window and/or push (depending on {@link #isDebug()} and
  181. * {@link #isPush()}.
  182. */
  183. protected void openTestURL() {
  184. driver.get(getTestUrl());
  185. }
  186. /**
  187. * Returns the full URL to be used for the test
  188. *
  189. * @return the full URL for the test
  190. */
  191. protected String getTestUrl() {
  192. String baseUrl = getBaseURL();
  193. if (baseUrl.endsWith("/")) {
  194. baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
  195. }
  196. return baseUrl + getDeploymentPath();
  197. }
  198. /**
  199. *
  200. * @return the location (URL) of the TB hub
  201. */
  202. protected String getHubURL() {
  203. return "http://" + getHubHostname() + ":4444/wd/hub";
  204. }
  205. /**
  206. * Used for building the hub URL to use for the test
  207. *
  208. * @return the host name of the TestBench hub
  209. */
  210. protected abstract String getHubHostname();
  211. /**
  212. * Used to determine what URL to initially open for the test
  213. *
  214. * @return the host name of development server
  215. */
  216. protected abstract String getDeploymentHostname();
  217. /**
  218. * Used to determine what port the test is running on
  219. *
  220. * @return The port teh test is running on, by default 8888
  221. */
  222. protected abstract int getDeploymentPort();
  223. /**
  224. * Produces a collection of browsers to run the test on. This method is
  225. * executed by the test runner when determining how many test methods to
  226. * invoke and with what parameters. For each returned value a test method is
  227. * ran and before running that,
  228. * {@link #setDesiredCapabilities(DesiredCapabilities)} is invoked with the
  229. * value returned by this method.
  230. *
  231. * This method is not static to allow overriding it in sub classes. By
  232. * default runs the test only on Firefox
  233. *
  234. * @return The browsers to run the test on
  235. */
  236. public List<DesiredCapabilities> getBrowsersToTest() {
  237. return Collections.singletonList(Browser.FIREFOX
  238. .getDesiredCapabilities());
  239. }
  240. /**
  241. * Used to determine which capabilities should be used when setting up a
  242. * {@link WebDriver} for this test. Typically set by a test runner or left
  243. * at its default (Firefox 24). If you want to run a test on a single
  244. * browser other than Firefox 24 you can override this method.
  245. *
  246. * @return the requested browser capabilities
  247. */
  248. protected DesiredCapabilities getDesiredCapabilities() {
  249. return desiredCapabilities;
  250. }
  251. /**
  252. * Sets the requested browser capabilities (typically browser name and
  253. * version)
  254. *
  255. * @param desiredCapabilities
  256. */
  257. public void setDesiredCapabilities(DesiredCapabilities desiredCapabilities) {
  258. this.desiredCapabilities = desiredCapabilities;
  259. }
  260. /**
  261. * Shuts down the driver after the test has been completed
  262. *
  263. * @throws Exception
  264. */
  265. @After
  266. public void tearDown() throws Exception {
  267. if (driver != null) {
  268. driver.quit();
  269. }
  270. driver = null;
  271. }
  272. /**
  273. * Finds an element based on the part of a TB2 style locator following the
  274. * :: (e.g. vaadin=runLabelModes::PID_Scheckboxaction-Enabled/domChild[0] ->
  275. * PID_Scheckboxaction-Enabled/domChild[0]).
  276. *
  277. * @param vaadinLocator
  278. * The part following :: of the vaadin locator string
  279. * @return
  280. */
  281. protected WebElement vaadinElement(String vaadinLocator) {
  282. return driver.findElement(vaadinLocator(vaadinLocator));
  283. }
  284. /**
  285. * Uses JavaScript to determine the currently focused element.
  286. *
  287. * @return Focused element or null
  288. */
  289. protected WebElement getFocusedElement() {
  290. Object focusedElement = ((JavascriptExecutor) getDriver())
  291. .executeScript("return document.activeElement");
  292. if (null != focusedElement) {
  293. return (WebElement) focusedElement;
  294. } else {
  295. return null;
  296. }
  297. }
  298. /**
  299. * Find a Vaadin element based on its id given using Component.setId
  300. *
  301. * @param id
  302. * The id to locate
  303. * @return
  304. */
  305. public WebElement vaadinElementById(String id) {
  306. return driver.findElement(vaadinLocatorById(id));
  307. }
  308. /**
  309. * Finds a {@link By} locator based on the part of a TB2 style locator
  310. * following the :: (e.g.
  311. * vaadin=runLabelModes::PID_Scheckboxaction-Enabled/domChild[0] ->
  312. * PID_Scheckboxaction-Enabled/domChild[0]).
  313. *
  314. * @param vaadinLocator
  315. * The part following :: of the vaadin locator string
  316. * @return
  317. */
  318. public org.openqa.selenium.By vaadinLocator(String vaadinLocator) {
  319. String base = getApplicationId(getDeploymentPath());
  320. base += "::";
  321. return com.vaadin.testbench.By.vaadin(base + vaadinLocator);
  322. }
  323. /**
  324. * Constructs a {@link By} locator for the id given using Component.setId
  325. *
  326. * @param id
  327. * The id to locate
  328. * @return a locator for the given id
  329. */
  330. public By vaadinLocatorById(String id) {
  331. return vaadinLocator("PID_S" + id);
  332. }
  333. /**
  334. * Waits up to 10s for the given condition to become true. Use e.g. as
  335. * {@link #waitUntil(ExpectedConditions.textToBePresentInElement(by, text))}
  336. *
  337. * @param condition
  338. * the condition to wait for to become true
  339. */
  340. protected void waitUntil(ExpectedCondition<Boolean> condition) {
  341. waitUntil(condition, 10);
  342. }
  343. /**
  344. * Waits the given number of seconds for the given condition to become true.
  345. * Use e.g. as {@link
  346. * #waitUntil(ExpectedConditions.textToBePresentInElement(by, text))}
  347. *
  348. * @param condition
  349. * the condition to wait for to become true
  350. */
  351. protected void waitUntil(ExpectedCondition<Boolean> condition,
  352. long timeoutInSeconds) {
  353. new WebDriverWait(driver, timeoutInSeconds).until(condition);
  354. }
  355. /**
  356. * Waits up to 10s for the given condition to become false. Use e.g. as
  357. * {@link #waitUntilNot(ExpectedConditions.textToBePresentInElement(by,
  358. * text))}
  359. *
  360. * @param condition
  361. * the condition to wait for to become false
  362. */
  363. protected void waitUntilNot(ExpectedCondition<Boolean> condition) {
  364. waitUntilNot(condition, 10);
  365. }
  366. /**
  367. * Waits the given number of seconds for the given condition to become
  368. * false. Use e.g. as {@link
  369. * #waitUntilNot(ExpectedConditions.textToBePresentInElement(by, text))}
  370. *
  371. * @param condition
  372. * the condition to wait for to become false
  373. */
  374. protected void waitUntilNot(ExpectedCondition<Boolean> condition,
  375. long timeoutInSeconds) {
  376. waitUntil(ExpectedConditions.not(condition), timeoutInSeconds);
  377. }
  378. protected void waitForElementToBePresent(By by) {
  379. waitUntil(ExpectedConditions.not(ExpectedConditions
  380. .invisibilityOfElementLocated(by)));
  381. }
  382. /**
  383. * For tests extending {@link AbstractTestUIWithLog}, returns the element
  384. * for the Nth log row
  385. *
  386. * @param rowNr
  387. * The log row to retrieve
  388. * @return the Nth log row
  389. */
  390. protected WebElement getLogRowElement(int rowNr) {
  391. return vaadinElementById("Log_row_" + rowNr);
  392. }
  393. /**
  394. * For tests extending {@link AbstractTestUIWithLog}, returns the text in
  395. * the Nth log row
  396. *
  397. * @param rowNr
  398. * The log row to retrieve text for
  399. * @return the text in the log row
  400. */
  401. protected String getLogRow(int rowNr) {
  402. return getLogRowElement(rowNr).getText();
  403. }
  404. /**
  405. * Asserts that {@literal a} is &gt;= {@literal b}
  406. *
  407. * @param message
  408. * The message to include in the {@link AssertionError}
  409. * @param a
  410. * @param b
  411. * @throws AssertionError
  412. * If comparison fails
  413. */
  414. public static final <T> void assertGreaterOrEqual(String message,
  415. Comparable<T> a, T b) throws AssertionError {
  416. if (a.compareTo(b) >= 0) {
  417. return;
  418. }
  419. throw new AssertionError(decorate(message, a, b));
  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 assertGreater(String message, Comparable<T> a,
  432. 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 &lt;= {@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 assertLessThanOrEqual(String message,
  449. Comparable<T> a, 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 assertLessThan(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. private static <T> String decorate(String message, Comparable<T> a, T b) {
  473. message = message.replace("{0}", a.toString());
  474. message = message.replace("{1}", b.toString());
  475. return message;
  476. }
  477. /**
  478. * Returns the path that should be used for the test. The path contains the
  479. * full path (appended to hostname+port) and must start with a slash.
  480. *
  481. * @param push
  482. * true if "?debug" should be added
  483. * @param debug
  484. * true if /run-push should be used instead of /run
  485. *
  486. * @return The URL path to the UI class to test
  487. */
  488. protected String getDeploymentPath() {
  489. Class<?> uiClass = getUIClass();
  490. if (uiClass != null) {
  491. return getDeploymentPath(uiClass);
  492. }
  493. throw new IllegalArgumentException("Unable to determine path for "
  494. + getClass().getCanonicalName());
  495. }
  496. /**
  497. * Returns the UI class the current test is connected to (or in special
  498. * cases UIProvider or LegacyApplication). Uses the enclosing class if the
  499. * test class is a static inner class to a UI class.
  500. *
  501. * Test which are not enclosed by a UI class must implement this method and
  502. * return the UI class they want to test.
  503. *
  504. * Note that this method will update the test name to the enclosing class to
  505. * be compatible with TB2 screenshot naming
  506. *
  507. * @return the UI class the current test is connected to
  508. */
  509. protected Class<?> getUIClass() {
  510. try {
  511. // Convention: SomeUITest uses the SomeUI UI class
  512. String uiClassName = getClass().getName().replaceFirst("Test$", "");
  513. Class<?> cls = Class.forName(uiClassName);
  514. if (isSupportedRunnerClass(cls)) {
  515. return cls;
  516. }
  517. } catch (Exception e) {
  518. }
  519. throw new RuntimeException(
  520. "Could not determine UI class. Ensure the test is named UIClassTest and is in the same package as the UIClass");
  521. }
  522. /**
  523. * @return true if the given class is supported by ApplicationServletRunner
  524. */
  525. @SuppressWarnings("deprecation")
  526. private boolean isSupportedRunnerClass(Class<?> cls) {
  527. if (UI.class.isAssignableFrom(cls)) {
  528. return true;
  529. }
  530. if (UIProvider.class.isAssignableFrom(cls)) {
  531. return true;
  532. }
  533. if (LegacyApplication.class.isAssignableFrom(cls)) {
  534. return true;
  535. }
  536. return false;
  537. }
  538. /**
  539. * Returns whether to run the test in debug mode (with the debug console
  540. * open) or not
  541. *
  542. * @return true to run with the debug window open, false by default
  543. */
  544. protected final boolean isDebug() {
  545. return debug;
  546. }
  547. /**
  548. * Sets whether to run the test in debug mode (with the debug console open)
  549. * or not.
  550. *
  551. * @param debug
  552. * true to open debug window, false otherwise
  553. */
  554. protected final void setDebug(boolean debug) {
  555. this.debug = debug;
  556. }
  557. /**
  558. * Returns whether to run the test with push enabled (using /run-push) or
  559. * not. Note that push tests can and should typically be created using @Push
  560. * on the UI instead of overriding this method
  561. *
  562. * @return true if /run-push is used, false otherwise
  563. */
  564. protected final boolean isPush() {
  565. return push;
  566. }
  567. /**
  568. * Sets whether to run the test with push enabled (using /run-push) or not.
  569. * Note that push tests can and should typically be created using @Push on
  570. * the UI instead of overriding this method
  571. *
  572. * @param push
  573. * true to use /run-push in the test, false otherwise
  574. */
  575. protected final void setPush(boolean push) {
  576. this.push = push;
  577. }
  578. /**
  579. * Returns the path for the given UI class when deployed on the test server.
  580. * The path contains the full path (appended to hostname+port) and must
  581. * start with a slash.
  582. *
  583. * This method takes into account {@link #isPush()} and {@link #isDebug()}
  584. * when the path is generated.
  585. *
  586. * @param uiClass
  587. * @param push
  588. * true if "?debug" should be added
  589. * @param debug
  590. * true if /run-push should be used instead of /run
  591. * @return The path to the given UI class
  592. */
  593. private String getDeploymentPath(Class<?> uiClass) {
  594. String runPath = "/run";
  595. if (isPush()) {
  596. runPath = "/run-push";
  597. }
  598. if (UI.class.isAssignableFrom(uiClass)) {
  599. return runPath + "/" + uiClass.getCanonicalName()
  600. + (isDebug() ? "?debug" : "");
  601. } else if (LegacyApplication.class.isAssignableFrom(uiClass)) {
  602. return runPath + "/" + uiClass.getCanonicalName()
  603. + "?restartApplication" + (isDebug() ? "&debug" : "");
  604. } else {
  605. throw new IllegalArgumentException(
  606. "Unable to determine path for enclosing class "
  607. + uiClass.getCanonicalName());
  608. }
  609. }
  610. /**
  611. * Used to determine what URL to initially open for the test
  612. *
  613. * @return The base URL for the test. Does not include a trailing slash.
  614. */
  615. protected String getBaseURL() {
  616. return "http://" + getDeploymentHostname() + ":" + getDeploymentPort();
  617. }
  618. /**
  619. * Generates the application id based on the URL in a way compatible with
  620. * VaadinServletService.
  621. *
  622. * @param pathWithQueryParameters
  623. * The path part of the URL, possibly still containing query
  624. * parameters
  625. * @return The application ID string used in Vaadin locators
  626. */
  627. private String getApplicationId(String pathWithQueryParameters) {
  628. // Remove any possible URL parameters
  629. String pathWithoutQueryParameters = pathWithQueryParameters.replaceAll(
  630. "\\?.*", "");
  631. if ("".equals(pathWithoutQueryParameters)) {
  632. return "ROOT";
  633. }
  634. // Retain only a-z and numbers
  635. return pathWithoutQueryParameters.replaceAll("[^a-zA-Z0-9]", "");
  636. }
  637. /**
  638. * Sleeps for the given number of ms but ensures that the browser connection
  639. * does not time out.
  640. *
  641. * @param timeoutMillis
  642. * Number of ms to wait
  643. * @throws InterruptedException
  644. */
  645. protected void sleep(int timeoutMillis) throws InterruptedException {
  646. while (timeoutMillis > 0) {
  647. int d = Math.min(BROWSER_TIMEOUT_IN_MS, timeoutMillis);
  648. Thread.sleep(d);
  649. timeoutMillis -= d;
  650. // Do something to keep the connection alive
  651. getDriver().getTitle();
  652. }
  653. }
  654. /**
  655. * Provides helper method for selecting the browser to run on
  656. *
  657. * @author Vaadin Ltd
  658. */
  659. public static class BrowserUtil {
  660. /**
  661. * Gets the capabilities for Safari of the given version
  662. *
  663. * @param version
  664. * the major version
  665. * @return an object describing the capabilities required for running a
  666. * test on the given Safari version
  667. */
  668. public static DesiredCapabilities safari(int version) {
  669. DesiredCapabilities c = DesiredCapabilities.safari();
  670. c.setPlatform(Platform.MAC);
  671. c.setVersion("" + version);
  672. return c;
  673. }
  674. /**
  675. * Gets the capabilities for Chrome of the given version
  676. *
  677. * @param version
  678. * the major version
  679. * @return an object describing the capabilities required for running a
  680. * test on the given Chrome version
  681. */
  682. public static DesiredCapabilities chrome(int version) {
  683. DesiredCapabilities c = DesiredCapabilities.chrome();
  684. c.setVersion("" + version);
  685. c.setPlatform(Platform.XP);
  686. return c;
  687. }
  688. /**
  689. * Gets the capabilities for Opera of the given version
  690. *
  691. * @param version
  692. * the major version
  693. * @return an object describing the capabilities required for running a
  694. * test on the given Opera version
  695. */
  696. public static DesiredCapabilities opera(int version) {
  697. DesiredCapabilities c = DesiredCapabilities.opera();
  698. c.setVersion("" + version);
  699. c.setPlatform(Platform.XP);
  700. return c;
  701. }
  702. /**
  703. * Gets the capabilities for Firefox of the given version
  704. *
  705. * @param version
  706. * the major version
  707. * @return an object describing the capabilities required for running a
  708. * test on the given Firefox version
  709. */
  710. public static DesiredCapabilities firefox(int version) {
  711. DesiredCapabilities c = DesiredCapabilities.firefox();
  712. c.setVersion("" + version);
  713. c.setPlatform(Platform.XP);
  714. return c;
  715. }
  716. /**
  717. * Gets the capabilities for Internet Explorer of the given version
  718. *
  719. * @param version
  720. * the major version
  721. * @return an object describing the capabilities required for running a
  722. * test on the given Internet Explorer version
  723. */
  724. public static DesiredCapabilities ie(int version) {
  725. DesiredCapabilities c = DesiredCapabilities.internetExplorer();
  726. c.setVersion("" + version);
  727. return c;
  728. }
  729. /**
  730. * Gets the capabilities for PhantomJS of the given version
  731. *
  732. * @param version
  733. * the major version
  734. * @return an object describing the capabilities required for running a
  735. * test on the given PhantomJS version
  736. */
  737. public static DesiredCapabilities phantomJS(int version) {
  738. DesiredCapabilities c = DesiredCapabilities.phantomjs();
  739. c.setPlatform(Platform.LINUX);
  740. c.setVersion("" + version);
  741. return c;
  742. }
  743. /**
  744. * Checks if the given capabilities refer to Internet Explorer 8
  745. *
  746. * @param capabilities
  747. * @return true if the capabilities refer to IE8, false otherwise
  748. */
  749. public static boolean isIE8(DesiredCapabilities capabilities) {
  750. return isIE(capabilities) && "8".equals(capabilities.getVersion());
  751. }
  752. /**
  753. * @param capabilities
  754. * The capabilities to check
  755. * @return true if the capabilities refer to Internet Explorer, false
  756. * otherwise
  757. */
  758. public static boolean isIE(DesiredCapabilities capabilities) {
  759. return BrowserType.IE.equals(capabilities.getBrowserName());
  760. }
  761. /**
  762. * @param capabilities
  763. * The capabilities to check
  764. * @return true if the capabilities refer to Chrome, false otherwise
  765. */
  766. public static boolean isChrome(DesiredCapabilities capabilities) {
  767. return BrowserType.CHROME.equals(capabilities.getBrowserName());
  768. }
  769. /**
  770. * @param capabilities
  771. * The capabilities to check
  772. * @return true if the capabilities refer to Opera, false otherwise
  773. */
  774. public static boolean isOpera(DesiredCapabilities capabilities) {
  775. return BrowserType.OPERA.equals(capabilities.getBrowserName());
  776. }
  777. /**
  778. * @param capabilities
  779. * The capabilities to check
  780. * @return true if the capabilities refer to Safari, false otherwise
  781. */
  782. public static boolean isSafari(DesiredCapabilities capabilities) {
  783. return BrowserType.SAFARI.equals(capabilities.getBrowserName());
  784. }
  785. /**
  786. * @param capabilities
  787. * The capabilities to check
  788. * @return true if the capabilities refer to Firefox, false otherwise
  789. */
  790. public static boolean isFirefox(DesiredCapabilities capabilities) {
  791. return BrowserType.FIREFOX.equals(capabilities.getBrowserName());
  792. }
  793. /**
  794. * @param capabilities
  795. * The capabilities to check
  796. * @return true if the capabilities refer to PhantomJS, false otherwise
  797. */
  798. public static boolean isPhantomJS(DesiredCapabilities capabilities) {
  799. return BrowserType.PHANTOMJS.equals(capabilities.getBrowserName());
  800. }
  801. /**
  802. * Returns a human readable identifier of the given browser. Used for
  803. * test naming and screenshots
  804. *
  805. * @param capabilities
  806. * @return a human readable string describing the capabilities
  807. */
  808. public static String getBrowserIdentifier(
  809. DesiredCapabilities capabilities) {
  810. if (isIE(capabilities)) {
  811. return "InternetExplorer";
  812. } else if (isFirefox(capabilities)) {
  813. return "Firefox";
  814. } else if (isChrome(capabilities)) {
  815. return "Chrome";
  816. } else if (isSafari(capabilities)) {
  817. return "Safari";
  818. } else if (isOpera(capabilities)) {
  819. return "Opera";
  820. } else if (isPhantomJS(capabilities)) {
  821. return "PhantomJS";
  822. }
  823. return capabilities.getBrowserName();
  824. }
  825. /**
  826. * Returns a human readable identifier of the platform described by the
  827. * given capabilities. Used mainly for screenshots
  828. *
  829. * @param capabilities
  830. * @return a human readable string describing the platform
  831. */
  832. public static String getPlatform(DesiredCapabilities capabilities) {
  833. if (capabilities.getPlatform() == Platform.WIN8
  834. || capabilities.getPlatform() == Platform.WINDOWS
  835. || capabilities.getPlatform() == Platform.VISTA
  836. || capabilities.getPlatform() == Platform.XP) {
  837. return "Windows";
  838. } else if (capabilities.getPlatform() == Platform.MAC) {
  839. return "Mac";
  840. }
  841. return capabilities.getPlatform().toString();
  842. }
  843. /**
  844. * Returns a string which uniquely (enough) identifies this browser.
  845. * Used mainly in screenshot names.
  846. *
  847. * @param capabilities
  848. *
  849. * @return a unique string for each browser
  850. */
  851. public static String getUniqueIdentifier(
  852. DesiredCapabilities capabilities) {
  853. return getUniqueIdentifier(getPlatform(capabilities),
  854. getBrowserIdentifier(capabilities),
  855. capabilities.getVersion());
  856. }
  857. /**
  858. * Returns a string which uniquely (enough) identifies this browser.
  859. * Used mainly in screenshot names.
  860. *
  861. * @param capabilities
  862. *
  863. * @return a unique string for each browser
  864. */
  865. public static String getUniqueIdentifier(
  866. DesiredCapabilities capabilities, String versionOverride) {
  867. return getUniqueIdentifier(getPlatform(capabilities),
  868. getBrowserIdentifier(capabilities), versionOverride);
  869. }
  870. private static String getUniqueIdentifier(String platform,
  871. String browser, String version) {
  872. return platform + "_" + browser + "_" + version;
  873. }
  874. }
  875. /**
  876. * Called by the test runner whenever there is an exception in the test that
  877. * will cause termination of the test
  878. *
  879. * @param t
  880. * the throwable which caused the termination
  881. */
  882. public void onUncaughtException(Throwable t) {
  883. // Do nothing by default
  884. }
  885. /**
  886. * Returns the mouse object for doing mouse commands
  887. *
  888. * @return Returns the mouse
  889. */
  890. public Mouse getMouse() {
  891. return ((HasInputDevices) getDriver()).getMouse();
  892. }
  893. /**
  894. * Returns the keyboard object for controlling keyboard events
  895. *
  896. * @return Return the keyboard
  897. */
  898. public Keyboard getKeyboard() {
  899. return ((HasInputDevices) getDriver()).getKeyboard();
  900. }
  901. public void hitButton(String id) {
  902. if (BrowserUtil.isPhantomJS(getDesiredCapabilities())) {
  903. driver.findElement(By.id(id)).click();
  904. } else {
  905. WebDriverBackedSelenium selenium = new WebDriverBackedSelenium(
  906. driver, driver.getCurrentUrl());
  907. selenium.keyPress("id=" + id, "\\13");
  908. }
  909. }
  910. protected void openDebugLogTab() {
  911. waitUntil(new ExpectedCondition<Boolean>() {
  912. @Override
  913. public Boolean apply(WebDriver input) {
  914. WebElement element = getDebugLogButton();
  915. return element != null;
  916. }
  917. }, 15);
  918. getDebugLogButton().click();
  919. }
  920. private WebElement getDebugLogButton() {
  921. return findElement(By.xpath("//button[@title='Debug message log']"));
  922. }
  923. }