Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

AbstractTB3Test.java 41KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  1. /*
  2. * Copyright 2000-2016 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 org.junit.Assert.assertEquals;
  18. import static org.junit.Assert.assertFalse;
  19. import static org.junit.Assert.assertTrue;
  20. import static org.junit.Assert.fail;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.StringWriter;
  24. import java.lang.reflect.Field;
  25. import java.net.URL;
  26. import java.util.ArrayList;
  27. import java.util.Arrays;
  28. import java.util.Collections;
  29. import java.util.HashSet;
  30. import java.util.List;
  31. import java.util.Set;
  32. import java.util.logging.Level;
  33. import org.apache.commons.io.IOUtils;
  34. import org.apache.commons.lang3.StringUtils;
  35. import org.apache.http.HttpHost;
  36. import org.apache.http.HttpResponse;
  37. import org.apache.http.impl.client.DefaultHttpClient;
  38. import org.apache.http.message.BasicHttpEntityEnclosingRequest;
  39. import org.junit.Rule;
  40. import org.junit.rules.TestName;
  41. import org.junit.runner.RunWith;
  42. import org.openqa.selenium.By;
  43. import org.openqa.selenium.Dimension;
  44. import org.openqa.selenium.JavascriptExecutor;
  45. import org.openqa.selenium.NoSuchElementException;
  46. import org.openqa.selenium.WebDriver;
  47. import org.openqa.selenium.WebElement;
  48. import org.openqa.selenium.interactions.Actions;
  49. import org.openqa.selenium.interactions.HasInputDevices;
  50. import org.openqa.selenium.interactions.Keyboard;
  51. import org.openqa.selenium.interactions.Mouse;
  52. import org.openqa.selenium.interactions.internal.Coordinates;
  53. import org.openqa.selenium.internal.Locatable;
  54. import org.openqa.selenium.internal.WrapsElement;
  55. import org.openqa.selenium.remote.DesiredCapabilities;
  56. import org.openqa.selenium.remote.HttpCommandExecutor;
  57. import org.openqa.selenium.remote.RemoteWebDriver;
  58. import org.openqa.selenium.support.ui.ExpectedCondition;
  59. import org.openqa.selenium.support.ui.ExpectedConditions;
  60. import org.openqa.selenium.support.ui.WebDriverWait;
  61. import com.vaadin.server.LegacyApplication;
  62. import com.vaadin.server.UIProvider;
  63. import com.vaadin.testbench.TestBenchDriverProxy;
  64. import com.vaadin.testbench.TestBenchElement;
  65. import com.vaadin.testbench.annotations.BrowserConfiguration;
  66. import com.vaadin.testbench.elements.CheckBoxElement;
  67. import com.vaadin.testbench.elements.LabelElement;
  68. import com.vaadin.testbench.elements.TableElement;
  69. import com.vaadin.testbench.elements.VerticalLayoutElement;
  70. import com.vaadin.testbench.parallel.Browser;
  71. import com.vaadin.testbench.parallel.BrowserUtil;
  72. import com.vaadin.testbench.parallel.ParallelTest;
  73. import com.vaadin.ui.UI;
  74. import elemental.json.JsonObject;
  75. import elemental.json.impl.JsonUtil;
  76. /**
  77. * Base class for TestBench 3+ tests. All TB3+ tests in the project should
  78. * extend this class.
  79. *
  80. * Provides:
  81. * <ul>
  82. * <li>Helpers for browser selection</li>
  83. * <li>Hub connection setup and teardown</li>
  84. * <li>Automatic generation of URL for a given test on the development server
  85. * using {@link #getUIClass()} or by automatically finding an enclosing UI class
  86. * and based on requested features, e.g. {@link #isDebug()},
  87. * {@link #isPush()}</li>
  88. * <li>Generic helpers for creating TB3+ tests</li>
  89. * </ul>
  90. *
  91. * @author Vaadin Ltd
  92. */
  93. @RunWith(TB3Runner.class)
  94. public abstract class AbstractTB3Test extends ParallelTest {
  95. @Rule
  96. public TestName testName = new TestName();
  97. @Rule
  98. public RetryOnFail retry = new RetryOnFail();
  99. /**
  100. * Height of the screenshots we want to capture
  101. */
  102. private static final int SCREENSHOT_HEIGHT = 850;
  103. /**
  104. * Width of the screenshots we want to capture
  105. */
  106. private static final int SCREENSHOT_WIDTH = 1500;
  107. /**
  108. * Timeout used by the TB grid
  109. */
  110. private static final int BROWSER_TIMEOUT_IN_MS = 30 * 1000;
  111. protected static DesiredCapabilities PHANTOMJS2() {
  112. DesiredCapabilities phantomjs2 = new VaadinBrowserFactory()
  113. .create(Browser.PHANTOMJS, "2");
  114. // Hack for the test cluster
  115. phantomjs2.setCapability("phantomjs.binary.path",
  116. "/usr/bin/phantomjs2");
  117. return phantomjs2;
  118. }
  119. private boolean debug = false;
  120. private boolean push = false;
  121. static {
  122. com.vaadin.testbench.Parameters
  123. .setScreenshotComparisonCursorDetection(true);
  124. }
  125. /**
  126. * Connect to the hub using a remote web driver, set the canvas size and
  127. * opens the initial URL as specified by {@link #getTestUrl()}
  128. *
  129. * @throws Exception
  130. */
  131. @Override
  132. public void setup() throws Exception {
  133. super.setup();
  134. int w = SCREENSHOT_WIDTH;
  135. int h = SCREENSHOT_HEIGHT;
  136. try {
  137. testBench().resizeViewPortTo(w, h);
  138. } catch (UnsupportedOperationException e) {
  139. // Opera does not support this...
  140. }
  141. }
  142. /**
  143. * Method for closing the tested application.
  144. */
  145. protected void closeApplication() {
  146. if (driver != null) {
  147. try {
  148. openTestURL("closeApplication");
  149. } catch (Exception e) {
  150. e.printStackTrace();
  151. }
  152. }
  153. }
  154. protected WebElement getTooltipErrorElement() {
  155. WebElement tooltip = getDriver()
  156. .findElement(com.vaadin.testbench.By.className("v-tooltip"));
  157. return tooltip.findElement(By.className("v-errormessage"));
  158. }
  159. protected WebElement getTooltipElement() {
  160. return getDriver().findElement(
  161. com.vaadin.testbench.By.className("v-tooltip-text"));
  162. }
  163. protected Coordinates getCoordinates(TestBenchElement element) {
  164. return ((Locatable) element.getWrappedElement()).getCoordinates();
  165. }
  166. private boolean hasDebugMessage(String message) {
  167. return getDebugMessage(message) != null;
  168. }
  169. private WebElement getDebugMessage(String message) {
  170. return driver.findElement(By.xpath(String.format(
  171. "//span[@class='v-debugwindow-message' and text()='%s']",
  172. message)));
  173. }
  174. protected void waitForDebugMessage(final String expectedMessage) {
  175. waitForDebugMessage(expectedMessage, 30);
  176. }
  177. protected void waitForDebugMessage(final String expectedMessage,
  178. int timeout) {
  179. waitUntil(new ExpectedCondition<Boolean>() {
  180. @Override
  181. public Boolean apply(WebDriver input) {
  182. return hasDebugMessage(expectedMessage);
  183. }
  184. }, timeout);
  185. }
  186. protected void clearDebugMessages() {
  187. driver.findElement(By.xpath(
  188. "//button[@class='v-debugwindow-button' and @title='Clear log']"))
  189. .click();
  190. }
  191. protected void waitUntilRowIsVisible(final TableElement table,
  192. final int row) {
  193. waitUntil(new ExpectedCondition<Object>() {
  194. @Override
  195. public Object apply(WebDriver input) {
  196. try {
  197. return table.getCell(row, 0) != null;
  198. } catch (NoSuchElementException e) {
  199. return false;
  200. }
  201. }
  202. });
  203. }
  204. protected void scrollTable(TableElement table, int rows, int rowToWait) {
  205. testBenchElement(table.findElement(By.className("v-scrollable")))
  206. .scroll(rows * 30);
  207. waitUntilRowIsVisible(table, rowToWait);
  208. }
  209. /**
  210. * Opens the given test (defined by {@link #getTestUrl()}, optionally with
  211. * debug window and/or push (depending on {@link #isDebug()} and
  212. * {@link #isPush()}.
  213. */
  214. protected void openTestURL() {
  215. openTestURL(new String[0]);
  216. }
  217. /**
  218. * Opens the given test (defined by {@link #getTestUrl()}, optionally with
  219. * debug window and/or push (depending on {@link #isDebug()} and
  220. * {@link #isPush()}.
  221. */
  222. protected void openTestURL(String... parameters) {
  223. openTestURL(getUIClass(), parameters);
  224. }
  225. /**
  226. * Opens the given test (defined by {@link #getTestUrl()}, optionally with
  227. * debug window and/or push (depending on {@link #isDebug()} and
  228. * {@link #isPush()}.
  229. */
  230. protected void openTestURL(Class<?> uiClass, String... parameters) {
  231. openTestURL(uiClass, new HashSet<>(Arrays.asList(parameters)));
  232. }
  233. private void openTestURL(Class<?> uiClass, Set<String> parameters) {
  234. String url = getTestURL(uiClass);
  235. if (isDebug()) {
  236. parameters.add("debug");
  237. }
  238. if (LegacyApplication.class.isAssignableFrom(uiClass)) {
  239. parameters.add("restartApplication");
  240. }
  241. if (!parameters.isEmpty()) {
  242. url += "?" + StringUtils.join(parameters, "&");
  243. }
  244. driver.get(url);
  245. }
  246. /**
  247. * Returns the full URL to be used for the test
  248. *
  249. * @return the full URL for the test
  250. */
  251. protected String getTestUrl() {
  252. return StringUtils.strip(getBaseURL(), "/") + getDeploymentPath();
  253. }
  254. /**
  255. * Returns the full URL to be used for the test for the provided UI class.
  256. *
  257. * @return the full URL for the test
  258. */
  259. protected String getTestURL(Class<?> uiClass) {
  260. return StringUtils.strip(getBaseURL(), "/")
  261. + getDeploymentPath(uiClass);
  262. }
  263. /**
  264. * Used to determine what URL to initially open for the test
  265. *
  266. * @return the host name of development server
  267. */
  268. protected abstract String getDeploymentHostname();
  269. /**
  270. * Used to determine what port the test is running on
  271. *
  272. * @return The port the test is running on, by default 8888
  273. */
  274. protected abstract int getDeploymentPort();
  275. /**
  276. * Produces a collection of browsers to run the test on. This method is
  277. * executed by the test runner when determining how many test methods to
  278. * invoke and with what parameters. For each returned value a test method is
  279. * ran and before running that,
  280. * {@link #setDesiredCapabilities(DesiredCapabilities)} is invoked with the
  281. * value returned by this method.
  282. *
  283. * This method is not static to allow overriding it in sub classes. By
  284. * default runs the test only on Firefox
  285. *
  286. * @return The browsers to run the test on
  287. */
  288. @BrowserConfiguration
  289. public List<DesiredCapabilities> getBrowsersToTest() {
  290. return Collections
  291. .singletonList(Browser.FIREFOX.getDesiredCapabilities());
  292. }
  293. /**
  294. * Finds an element based on the part of a TB2 style locator following the
  295. * :: (e.g. vaadin=runLabelModes::PID_Scheckboxaction-Enabled/domChild[0] ->
  296. * PID_Scheckboxaction-Enabled/domChild[0]).
  297. *
  298. * @param vaadinLocator
  299. * The part following :: of the vaadin locator string
  300. * @return
  301. */
  302. protected WebElement vaadinElement(String vaadinLocator) {
  303. return driver.findElement(vaadinLocator(vaadinLocator));
  304. }
  305. /**
  306. * Uses JavaScript to determine the currently focused element.
  307. *
  308. * @return Focused element or null
  309. */
  310. protected WebElement getFocusedElement() {
  311. Object focusedElement = executeScript("return document.activeElement");
  312. if (null != focusedElement) {
  313. return (WebElement) focusedElement;
  314. } else {
  315. return null;
  316. }
  317. }
  318. /**
  319. * Executes the given Javascript
  320. *
  321. * @param script
  322. * the script to execute
  323. * @return whatever
  324. * {@link org.openqa.selenium.JavascriptExecutor#executeScript(String, Object...)}
  325. * returns
  326. */
  327. protected Object executeScript(String script, Object... args) {
  328. return ((JavascriptExecutor) getDriver()).executeScript(script, args);
  329. }
  330. /**
  331. * Find a Vaadin element based on its id given using Component.setId
  332. *
  333. * @param id
  334. * The id to locate
  335. * @return
  336. */
  337. public WebElement vaadinElementById(String id) {
  338. return driver.findElement(By.id(id));
  339. }
  340. /**
  341. * Finds a {@link By} locator based on the part of a TB2 style locator
  342. * following the :: (e.g.
  343. * vaadin=runLabelModes::PID_Scheckboxaction-Enabled/domChild[0] ->
  344. * PID_Scheckboxaction-Enabled/domChild[0]).
  345. *
  346. * @param vaadinLocator
  347. * The part following :: of the vaadin locator string
  348. * @return
  349. */
  350. public org.openqa.selenium.By vaadinLocator(String vaadinLocator) {
  351. String base = getApplicationId(getDeploymentPath());
  352. base += "::";
  353. return com.vaadin.testbench.By.vaadin(base + vaadinLocator);
  354. }
  355. /**
  356. * Constructs a {@link By} locator for the id given using Component.setId
  357. *
  358. * @param id
  359. * The id to locate
  360. * @return a locator for the given id
  361. */
  362. public By vaadinLocatorById(String id) {
  363. return vaadinLocator("PID_S" + id);
  364. }
  365. /**
  366. * Waits up to 10s for the given condition to become true. Use e.g. as
  367. * {@link #waitUntil(ExpectedConditions.textToBePresentInElement(by, text))}
  368. *
  369. * @param condition
  370. * the condition to wait for to become true
  371. */
  372. protected <T> void waitUntil(ExpectedCondition<T> condition) {
  373. waitUntil(condition, 10);
  374. }
  375. /**
  376. * Waits the given number of seconds for the given condition to become true.
  377. * Use e.g. as
  378. * {@link #waitUntil(ExpectedConditions.textToBePresentInElement(by, text))}
  379. *
  380. * @param condition
  381. * the condition to wait for to become true
  382. */
  383. protected <T> void waitUntil(ExpectedCondition<T> condition,
  384. long timeoutInSeconds) {
  385. new WebDriverWait(driver, timeoutInSeconds).until(condition);
  386. }
  387. /**
  388. * Waits up to 10s for the given condition to become false. Use e.g. as
  389. * {@link #waitUntilNot(ExpectedConditions.textToBePresentInElement(by,
  390. * text))}
  391. *
  392. * @param condition
  393. * the condition to wait for to become false
  394. */
  395. protected <T> void waitUntilNot(ExpectedCondition<T> condition) {
  396. waitUntilNot(condition, 10);
  397. }
  398. /**
  399. * Waits the given number of seconds for the given condition to become
  400. * false. Use e.g. as
  401. * {@link #waitUntilNot(ExpectedConditions.textToBePresentInElement(by,
  402. * text))}
  403. *
  404. * @param condition
  405. * the condition to wait for to become false
  406. */
  407. protected <T> void waitUntilNot(ExpectedCondition<T> condition,
  408. long timeoutInSeconds) {
  409. waitUntil(ExpectedConditions.not(condition), timeoutInSeconds);
  410. }
  411. protected void waitForElementPresent(final By by) {
  412. waitUntil(ExpectedConditions.presenceOfElementLocated(by));
  413. }
  414. protected void waitForElementNotPresent(final By by) {
  415. waitUntil(new ExpectedCondition<Boolean>() {
  416. @Override
  417. public Boolean apply(WebDriver input) {
  418. return input.findElements(by).isEmpty();
  419. }
  420. });
  421. }
  422. protected void waitForElementVisible(final By by) {
  423. waitUntil(ExpectedConditions.visibilityOfElementLocated(by));
  424. }
  425. /**
  426. * Checks if the given element has the given class name.
  427. *
  428. * Matches only full class names, i.e. has ("foo") does not match
  429. * class="foobar"
  430. *
  431. * @param element
  432. * @param className
  433. * @return
  434. */
  435. protected boolean hasCssClass(WebElement element, String className) {
  436. String classes = element.getAttribute("class");
  437. if (classes == null || classes.isEmpty()) {
  438. return (className == null || className.isEmpty());
  439. }
  440. for (String cls : classes.split(" ")) {
  441. if (className.equals(cls)) {
  442. return true;
  443. }
  444. }
  445. return false;
  446. }
  447. /**
  448. * For tests extending AbstractTestUIWithLog, returns the element for the
  449. * Nth log row
  450. *
  451. * @param rowNr
  452. * The log row to retrieve
  453. * @return the Nth log row
  454. */
  455. protected WebElement getLogRowElement(int rowNr) {
  456. return vaadinElementById("Log_row_" + rowNr);
  457. }
  458. /**
  459. * For tests extending AbstractTestUIWithLog, returns the text in the Nth
  460. * log row
  461. *
  462. * @param rowNr
  463. * The log row to retrieve text for
  464. * @return the text in the log row
  465. */
  466. protected String getLogRow(int rowNr) {
  467. return getLogRowElement(rowNr).getText();
  468. }
  469. /**
  470. * Asserts that {@literal a} is &gt;= {@literal b}
  471. *
  472. * @param message
  473. * The message to include in the {@link AssertionError}
  474. * @param a
  475. * @param b
  476. * @throws AssertionError
  477. * If comparison fails
  478. */
  479. public static final <T> void assertGreaterOrEqual(String message,
  480. Comparable<T> a, T b) throws AssertionError {
  481. if (a.compareTo(b) >= 0) {
  482. return;
  483. }
  484. throw new AssertionError(decorate(message, a, b));
  485. }
  486. /**
  487. * Asserts that {@literal a} is &gt; {@literal b}
  488. *
  489. * @param message
  490. * The message to include in the {@link AssertionError}
  491. * @param a
  492. * @param b
  493. * @throws AssertionError
  494. * If comparison fails
  495. */
  496. public static final <T> void assertGreater(String message, Comparable<T> a,
  497. T b) throws AssertionError {
  498. if (a.compareTo(b) > 0) {
  499. return;
  500. }
  501. throw new AssertionError(decorate(message, a, b));
  502. }
  503. /**
  504. * Asserts that {@literal a} is &lt;= {@literal b}
  505. *
  506. * @param message
  507. * The message to include in the {@link AssertionError}
  508. * @param a
  509. * @param b
  510. * @throws AssertionError
  511. * If comparison fails
  512. */
  513. public static final <T> void assertLessThanOrEqual(String message,
  514. Comparable<T> a, T b) throws AssertionError {
  515. if (a.compareTo(b) <= 0) {
  516. return;
  517. }
  518. throw new AssertionError(decorate(message, a, b));
  519. }
  520. /**
  521. * Asserts that {@literal a} is &lt; {@literal b}
  522. *
  523. * @param message
  524. * The message to include in the {@link AssertionError}
  525. * @param a
  526. * @param b
  527. * @throws AssertionError
  528. * If comparison fails
  529. */
  530. public static final <T> void assertLessThan(String message, Comparable<T> a,
  531. T b) throws AssertionError {
  532. if (a.compareTo(b) < 0) {
  533. return;
  534. }
  535. throw new AssertionError(decorate(message, a, b));
  536. }
  537. private static <T> String decorate(String message, Comparable<T> a, T b) {
  538. message = message.replace("{0}", a.toString());
  539. message = message.replace("{1}", b.toString());
  540. return message;
  541. }
  542. /**
  543. * Returns the path that should be used for the test. The path contains the
  544. * full path (appended to hostname+port) and must start with a slash.
  545. *
  546. * @param push
  547. * true if "?debug" should be added
  548. * @param debug
  549. * true if /run-push should be used instead of /run
  550. *
  551. * @return The URL path to the UI class to test
  552. */
  553. protected String getDeploymentPath() {
  554. Class<?> uiClass = getUIClass();
  555. if (uiClass != null) {
  556. return getDeploymentPath(uiClass);
  557. }
  558. throw new IllegalArgumentException("Unable to determine path for "
  559. + getClass().getCanonicalName());
  560. }
  561. /**
  562. * Returns the UI class the current test is connected to (or in special
  563. * cases UIProvider or LegacyApplication). Uses the enclosing class if the
  564. * test class is a static inner class to a UI class.
  565. *
  566. * Test which are not enclosed by a UI class must implement this method and
  567. * return the UI class they want to test.
  568. *
  569. * Note that this method will update the test name to the enclosing class to
  570. * be compatible with TB2 screenshot naming
  571. *
  572. * @return the UI class the current test is connected to
  573. */
  574. protected Class<?> getUIClass() {
  575. try {
  576. // Convention: SomeUITest uses the SomeUI UI class
  577. String uiClassName = getClass().getName().replaceFirst("Test$", "");
  578. Class<?> cls = Class.forName(uiClassName);
  579. if (isSupportedRunnerClass(cls)) {
  580. return cls;
  581. }
  582. } catch (Exception e) {
  583. }
  584. throw new RuntimeException(
  585. "Could not determine UI class. Ensure the test is named UIClassTest and is in the same package as the UIClass");
  586. }
  587. /**
  588. * @return true if the given class is supported by ApplicationServletRunner
  589. */
  590. @SuppressWarnings("deprecation")
  591. private boolean isSupportedRunnerClass(Class<?> cls) {
  592. if (UI.class.isAssignableFrom(cls)) {
  593. return true;
  594. }
  595. if (UIProvider.class.isAssignableFrom(cls)) {
  596. return true;
  597. }
  598. if (LegacyApplication.class.isAssignableFrom(cls)) {
  599. return true;
  600. }
  601. return false;
  602. }
  603. /**
  604. * Returns whether to run the test in debug mode (with the debug console
  605. * open) or not
  606. *
  607. * @return true to run with the debug window open, false by default
  608. */
  609. protected final boolean isDebug() {
  610. return debug;
  611. }
  612. /**
  613. * Sets whether to run the test in debug mode (with the debug console open)
  614. * or not.
  615. *
  616. * @param debug
  617. * true to open debug window, false otherwise
  618. */
  619. protected final void setDebug(boolean debug) {
  620. this.debug = debug;
  621. }
  622. /**
  623. * Returns whether to run the test with push enabled (using /run-push) or
  624. * not. Note that push tests can and should typically be created using @Push
  625. * on the UI instead of overriding this method
  626. *
  627. * @return true if /run-push is used, false otherwise
  628. */
  629. protected final boolean isPush() {
  630. return push;
  631. }
  632. /**
  633. * Sets whether to run the test with push enabled (using /run-push) or not.
  634. * Note that push tests can and should typically be created using @Push on
  635. * the UI instead of overriding this method
  636. *
  637. * @param push
  638. * true to use /run-push in the test, false otherwise
  639. */
  640. protected final void setPush(boolean push) {
  641. this.push = push;
  642. }
  643. /**
  644. * Returns the path for the given UI class when deployed on the test server.
  645. * The path contains the full path (appended to hostname+port) and must
  646. * start with a slash.
  647. *
  648. * This method takes into account {@link #isPush()} and {@link #isDebug()}
  649. * when the path is generated.
  650. *
  651. * @param uiClass
  652. * @param push
  653. * true if "?debug" should be added
  654. * @param debug
  655. * true if /run-push should be used instead of /run
  656. * @return The path to the given UI class
  657. */
  658. protected String getDeploymentPath(Class<?> uiClass) {
  659. String runPath = "/run";
  660. if (isPush()) {
  661. runPath = "/run-push";
  662. }
  663. if (UI.class.isAssignableFrom(uiClass)
  664. || UIProvider.class.isAssignableFrom(uiClass)
  665. || LegacyApplication.class.isAssignableFrom(uiClass)) {
  666. return runPath + "/" + uiClass.getCanonicalName();
  667. } else {
  668. throw new IllegalArgumentException(
  669. "Unable to determine path for enclosing class "
  670. + uiClass.getCanonicalName());
  671. }
  672. }
  673. /**
  674. * Used to determine what URL to initially open for the test
  675. *
  676. * @return The base URL for the test. Does not include a trailing slash.
  677. */
  678. protected String getBaseURL() {
  679. return "http://" + getDeploymentHostname() + ":" + getDeploymentPort();
  680. }
  681. /**
  682. * Generates the application id based on the URL in a way compatible with
  683. * VaadinServletService.
  684. *
  685. * @param pathWithQueryParameters
  686. * The path part of the URL, possibly still containing query
  687. * parameters
  688. * @return The application ID string used in Vaadin locators
  689. */
  690. private String getApplicationId(String pathWithQueryParameters) {
  691. // Remove any possible URL parameters
  692. String pathWithoutQueryParameters = pathWithQueryParameters
  693. .replaceAll("\\?.*", "");
  694. if (pathWithoutQueryParameters.isEmpty()) {
  695. return "ROOT";
  696. }
  697. // Retain only a-z and numbers
  698. return pathWithoutQueryParameters.replaceAll("[^a-zA-Z0-9]", "");
  699. }
  700. /**
  701. * Sleeps for the given number of ms but ensures that the browser connection
  702. * does not time out.
  703. *
  704. * @param timeoutMillis
  705. * Number of ms to wait
  706. */
  707. protected void sleep(int timeoutMillis) {
  708. while (timeoutMillis > 0) {
  709. int d = Math.min(BROWSER_TIMEOUT_IN_MS, timeoutMillis);
  710. try {
  711. Thread.sleep(d);
  712. } catch (InterruptedException e) {
  713. throw new RuntimeException(e);
  714. }
  715. timeoutMillis -= d;
  716. // Do something to keep the connection alive
  717. getDriver().getTitle();
  718. }
  719. }
  720. /**
  721. * Called by the test runner whenever there is an exception in the test that
  722. * will cause termination of the test
  723. *
  724. * @param t
  725. * the throwable which caused the termination
  726. */
  727. public void onUncaughtException(Throwable t) {
  728. // Do nothing by default
  729. }
  730. /**
  731. * Returns the mouse object for doing mouse commands
  732. *
  733. * @return Returns the mouse
  734. */
  735. public Mouse getMouse() {
  736. return ((HasInputDevices) getDriver()).getMouse();
  737. }
  738. /**
  739. * Returns the keyboard object for controlling keyboard events
  740. *
  741. * @return Return the keyboard
  742. */
  743. public Keyboard getKeyboard() {
  744. return ((HasInputDevices) getDriver()).getKeyboard();
  745. }
  746. public void hitButton(String id) {
  747. driver.findElement(By.id(id)).click();
  748. }
  749. protected void openDebugLogTab() {
  750. waitUntil(new ExpectedCondition<Boolean>() {
  751. @Override
  752. public Boolean apply(WebDriver input) {
  753. WebElement element = getDebugLogButton();
  754. return element != null;
  755. }
  756. }, 15);
  757. getDebugLogButton().click();
  758. }
  759. private WebElement getDebugLogButton() {
  760. return findElement(By.xpath("//button[@title='Debug message log']"));
  761. }
  762. protected void assertNoDebugMessage(Level level) {
  763. // class="v-debugwindow-row Level.getName()"
  764. List<WebElement> logElements = driver.findElements(By.xpath(String
  765. .format("//div[@class='v-debugwindow-row %s']/span[@class='v-debugwindow-message']",
  766. level.getName())));
  767. if (!logElements.isEmpty()) {
  768. String logRows = "";
  769. for (WebElement e : logElements) {
  770. logRows += "\n" + e.getText();
  771. }
  772. fail("Found debug messages with level " + level.getName() + ": "
  773. + logRows);
  774. }
  775. }
  776. /**
  777. * Should the "require window focus" be enabled for Internet Explorer.
  778. * RequireWindowFocus makes tests more stable but seems to be broken with
  779. * certain commands such as sendKeys. Therefore it is not enabled by default
  780. * for all tests
  781. *
  782. * @return true, to use the "require window focus" feature, false otherwise
  783. */
  784. protected boolean requireWindowFocusForIE() {
  785. return false;
  786. }
  787. /**
  788. * Should the "enable persistent hover" be enabled for Internet Explorer.
  789. *
  790. * Persistent hovering causes continuous firing of mouse over events at the
  791. * last location the mouse cursor has been moved to. This is to avoid
  792. * problems where the real mouse cursor is inside the browser window and
  793. * Internet Explorer uses that location for some undefined operation
  794. * (http://
  795. * jimevansmusic.blogspot.fi/2012/06/whats-wrong-with-internet-explorer
  796. * .html)
  797. *
  798. * @return true, to use the "persistent hover" feature, false otherwise
  799. */
  800. protected boolean usePersistentHoverForIE() {
  801. return true;
  802. }
  803. /**
  804. * Should the "native events" be enabled for Internet Explorer.
  805. * <p>
  806. * Native events sometimes cause failure in clicking on buttons/checkboxes
  807. * but are possibly needed for some operations.
  808. *
  809. * @return true, to use "native events", false to use generated Javascript
  810. * events
  811. */
  812. protected boolean useNativeEventsForIE() {
  813. return true;
  814. }
  815. // FIXME: Remove this once TB4 getRemoteControlName works properly
  816. private RemoteWebDriver getRemoteDriver() {
  817. WebDriver d = getDriver();
  818. if (d instanceof TestBenchDriverProxy) {
  819. try {
  820. Field f = TestBenchDriverProxy.class
  821. .getDeclaredField("actualDriver");
  822. f.setAccessible(true);
  823. return (RemoteWebDriver) f.get(d);
  824. } catch (Exception e) {
  825. e.printStackTrace();
  826. }
  827. }
  828. if (d instanceof RemoteWebDriver) {
  829. return (RemoteWebDriver) d;
  830. }
  831. return null;
  832. }
  833. // FIXME: Remove this once TB4 getRemoteControlName works properly
  834. protected String getRemoteControlName() {
  835. try {
  836. RemoteWebDriver d = getRemoteDriver();
  837. if (d == null) {
  838. return null;
  839. }
  840. HttpCommandExecutor ce = (HttpCommandExecutor) d
  841. .getCommandExecutor();
  842. String hostName = ce.getAddressOfRemoteServer().getHost();
  843. int port = ce.getAddressOfRemoteServer().getPort();
  844. HttpHost host = new HttpHost(hostName, port);
  845. try (DefaultHttpClient client = new DefaultHttpClient()) {
  846. URL sessionURL = new URL("http://" + hostName + ":" + port
  847. + "/grid/api/testsession?session=" + d.getSessionId());
  848. BasicHttpEntityEnclosingRequest r = new BasicHttpEntityEnclosingRequest(
  849. "POST", sessionURL.toExternalForm());
  850. HttpResponse response = client.execute(host, r);
  851. JsonObject object = extractObject(response);
  852. URL myURL = new URL(object.getString("proxyId"));
  853. if ((myURL.getHost() != null) && (myURL.getPort() != -1)) {
  854. return myURL.getHost();
  855. }
  856. }
  857. } catch (Exception e) {
  858. e.printStackTrace();
  859. }
  860. return null;
  861. }
  862. protected boolean logContainsText(String string) {
  863. List<String> logs = getLogs();
  864. for (String text : logs) {
  865. if (text.contains(string)) {
  866. return true;
  867. }
  868. }
  869. return false;
  870. }
  871. protected List<String> getLogs() {
  872. VerticalLayoutElement log = $(VerticalLayoutElement.class).id("Log");
  873. List<LabelElement> logLabels = log.$(LabelElement.class).all();
  874. List<String> logTexts = new ArrayList<>();
  875. for (LabelElement label : logLabels) {
  876. logTexts.add(label.getText());
  877. }
  878. return logTexts;
  879. }
  880. private static JsonObject extractObject(HttpResponse resp)
  881. throws IOException {
  882. InputStream contents = resp.getEntity().getContent();
  883. StringWriter writer = new StringWriter();
  884. IOUtils.copy(contents, writer, "UTF8");
  885. return JsonUtil.parse(writer.toString());
  886. }
  887. protected void click(CheckBoxElement checkbox) {
  888. WebElement cb = checkbox.findElement(By.xpath("input"));
  889. if (BrowserUtil.isChrome(getDesiredCapabilities())) {
  890. testBenchElement(cb).click(0, 0);
  891. } else if (BrowserUtil.isFirefox(getDesiredCapabilities())) {
  892. // Firefox workaround
  893. getCommandExecutor().executeScript("arguments[0].click()", cb);
  894. } else {
  895. cb.click();
  896. }
  897. }
  898. protected void clickElement(WebElement element) {
  899. if (BrowserUtil.isFirefox(getDesiredCapabilities())) {
  900. // Workaround for Selenium/TB and Firefox 45 issue
  901. ((TestBenchElement) (element)).clickHiddenElement();
  902. } else {
  903. element.click();
  904. }
  905. }
  906. protected void contextClickElement(WebElement element) {
  907. if (BrowserUtil.isFirefox(getDesiredCapabilities())) {
  908. // Workaround for Selenium/TB and Firefox 45 issue
  909. getCommandExecutor().executeScript(
  910. "var ev = document.createEvent('HTMLEvents'); ev.initEvent('contextmenu', true, false); arguments[0].dispatchEvent(ev);",
  911. element);
  912. } else {
  913. new Actions(getDriver()).contextClick(element).perform();
  914. }
  915. }
  916. protected boolean isLoadingIndicatorVisible() {
  917. WebElement loadingIndicator = findElement(
  918. By.className("v-loading-indicator"));
  919. return loadingIndicator.isDisplayed();
  920. }
  921. protected void waitUntilLoadingIndicatorVisible() {
  922. waitUntil(input -> isLoadingIndicatorVisible());
  923. }
  924. protected void waitUntilLoadingIndicatorNotVisible() {
  925. waitUntil(input -> !isLoadingIndicatorVisible());
  926. }
  927. /**
  928. * Selects a menu item. By default, this will click on the menu item.
  929. *
  930. * @param menuCaption
  931. * caption of the menu item
  932. */
  933. protected void selectMenu(String menuCaption) {
  934. selectMenu(menuCaption, true);
  935. }
  936. /**
  937. * Selects a menu item.
  938. *
  939. * @param menuCaption
  940. * caption of the menu item
  941. * @param click
  942. * <code>true</code> if should click the menu item;
  943. * <code>false</code> if not
  944. */
  945. protected void selectMenu(String menuCaption, boolean click) {
  946. WebElement menuElement = getMenuElement(menuCaption);
  947. Dimension size = menuElement.getSize();
  948. new Actions(getDriver())
  949. .moveToElement(menuElement, size.width - 10, size.height / 2)
  950. .perform();
  951. if (click) {
  952. new Actions(getDriver()).click().perform();
  953. }
  954. }
  955. /**
  956. * Finds the menu item from the DOM based on menu item caption.
  957. *
  958. * @param menuCaption
  959. * caption of the menu item
  960. * @return the found menu item
  961. * @throws NoSuchElementException
  962. * if menu item is not found
  963. */
  964. protected WebElement getMenuElement(String menuCaption)
  965. throws NoSuchElementException {
  966. // Need the parent span to obtain the correct size
  967. return getDriver().findElement(
  968. By.xpath("//span[text() = '" + menuCaption + "']/.."));
  969. }
  970. /**
  971. * Selects a submenu described by a path of menus from the first MenuBar in
  972. * the UI.
  973. *
  974. * @param menuCaptions
  975. * array of menu captions
  976. */
  977. protected void selectMenuPath(String... menuCaptions) {
  978. selectMenu(menuCaptions[0], true);
  979. // Move to the menu item opened below the menu bar.
  980. new Actions(getDriver())
  981. .moveByOffset(0,
  982. getMenuElement(menuCaptions[0]).getSize().getHeight())
  983. .perform();
  984. for (int i = 1; i < menuCaptions.length - 1; i++) {
  985. selectMenu(menuCaptions[i]);
  986. new Actions(getDriver()).moveByOffset(40, 0).build().perform();
  987. }
  988. selectMenu(menuCaptions[menuCaptions.length - 1], true);
  989. }
  990. /**
  991. * Asserts that an element is present
  992. *
  993. * @param by
  994. * the locator for the element
  995. */
  996. protected void assertElementPresent(By by) {
  997. assertTrue("Element is not present", isElementPresent(by));
  998. }
  999. /**
  1000. * Asserts that an element is not present
  1001. *
  1002. * @param by
  1003. * the locator for the element
  1004. */
  1005. protected void assertElementNotPresent(By by) {
  1006. assertFalse("Element is present", isElementPresent(by));
  1007. }
  1008. /**
  1009. * Asserts that no error notifications are shown. Requires the use of
  1010. * "?debug" as exceptions are otherwise not shown as notifications.
  1011. */
  1012. protected void assertNoErrorNotifications() {
  1013. assertFalse("Error notification with client side exception is shown",
  1014. isNotificationPresent("error"));
  1015. }
  1016. /**
  1017. * Asserts that no system notifications are shown.
  1018. */
  1019. protected void assertNoSystemNotifications() {
  1020. assertFalse("Error notification with system error exception is shown",
  1021. isNotificationPresent("system"));
  1022. }
  1023. /**
  1024. * Asserts that a system notification is shown.
  1025. */
  1026. protected void assertSystemNotification() {
  1027. assertTrue(
  1028. "Error notification with system error exception is not shown",
  1029. isNotificationPresent("system"));
  1030. }
  1031. private boolean isNotificationPresent(String type) {
  1032. if ("error".equals(type)) {
  1033. assertTrue(
  1034. "Debug window must be open to be able to see error notifications",
  1035. isDebugWindowOpen());
  1036. }
  1037. return isElementPresent(By.className("v-Notification-" + type));
  1038. }
  1039. private boolean isDebugWindowOpen() {
  1040. return isElementPresent(By.className("v-debugwindow"));
  1041. }
  1042. protected void assertNoHorizontalScrollbar(WebElement element,
  1043. String errorMessage) {
  1044. assertHasHorizontalScrollbar(element, errorMessage, false);
  1045. }
  1046. protected void assertHorizontalScrollbar(WebElement element,
  1047. String errorMessage) {
  1048. assertHasHorizontalScrollbar(element, errorMessage, true);
  1049. }
  1050. private void assertHasHorizontalScrollbar(WebElement element,
  1051. String errorMessage, boolean expected) {
  1052. // IE rounds clientWidth/clientHeight down and scrollHeight/scrollWidth
  1053. // up, so using clientWidth/clientHeight will fail if the element height
  1054. // is not an integer
  1055. int clientWidth = getClientWidth(element);
  1056. int scrollWidth = getScrollWidth(element);
  1057. boolean hasScrollbar = scrollWidth > clientWidth;
  1058. String message = "The element should";
  1059. if (!expected) {
  1060. message += " not";
  1061. }
  1062. message += " have a horizontal scrollbar (scrollWidth: " + scrollWidth
  1063. + ", clientWidth: " + clientWidth + "): " + errorMessage;
  1064. assertEquals(message, expected, hasScrollbar);
  1065. }
  1066. protected void assertNoVerticalScrollbar(WebElement element,
  1067. String errorMessage) {
  1068. // IE rounds clientWidth/clientHeight down and scrollHeight/scrollWidth
  1069. // up, so using clientWidth/clientHeight will fail if the element height
  1070. // is not an integer
  1071. int clientHeight = getClientHeight(element);
  1072. int scrollHeight = getScrollHeight(element);
  1073. boolean hasScrollbar = scrollHeight > clientHeight;
  1074. assertFalse(
  1075. "The element should not have a vertical scrollbar (scrollHeight: "
  1076. + scrollHeight + ", clientHeight: " + clientHeight
  1077. + "): " + errorMessage,
  1078. hasScrollbar);
  1079. }
  1080. protected int getScrollHeight(WebElement element) {
  1081. return ((Number) executeScript("return arguments[0].scrollHeight;",
  1082. element)).intValue();
  1083. }
  1084. protected int getScrollWidth(WebElement element) {
  1085. return ((Number) executeScript("return arguments[0].scrollWidth;",
  1086. element)).intValue();
  1087. }
  1088. /**
  1089. * Returns client height rounded up instead of as double because of IE9
  1090. * issues: https://dev.vaadin.com/ticket/18469
  1091. */
  1092. protected int getClientHeight(WebElement e) {
  1093. String script = "var cs = window.getComputedStyle(arguments[0]);"
  1094. + "return Math.ceil(parseFloat(cs.height)+parseFloat(cs.paddingTop)+parseFloat(cs.paddingBottom));";
  1095. return ((Number) executeScript(script, e)).intValue();
  1096. }
  1097. /**
  1098. * Returns client width rounded up instead of as double because of IE9
  1099. * issues: https://dev.vaadin.com/ticket/18469
  1100. */
  1101. protected int getClientWidth(WebElement e) {
  1102. String script = "var cs = window.getComputedStyle(arguments[0]);"
  1103. + "var h = parseFloat(cs.width)+parseFloat(cs.paddingLeft)+parseFloat(cs.paddingRight);"
  1104. + "return Math.ceil(h);";
  1105. return ((Number) executeScript(script, e)).intValue();
  1106. }
  1107. protected void assertElementsEquals(WebElement expectedElement,
  1108. WebElement actualElement) {
  1109. while (expectedElement instanceof WrapsElement) {
  1110. expectedElement = ((WrapsElement) expectedElement)
  1111. .getWrappedElement();
  1112. }
  1113. while (actualElement instanceof WrapsElement) {
  1114. actualElement = ((WrapsElement) actualElement).getWrappedElement();
  1115. }
  1116. assertEquals(expectedElement, actualElement);
  1117. }
  1118. protected WebElement getActiveElement() {
  1119. return (WebElement) executeScript("return document.activeElement;");
  1120. }
  1121. protected void waitForThemeToChange(final String theme) {
  1122. final WebElement rootDiv = findElement(
  1123. By.xpath("//div[contains(@class,'v-app')]"));
  1124. waitUntil(new ExpectedCondition<Boolean>() {
  1125. @Override
  1126. public Boolean apply(WebDriver input) {
  1127. String rootClass = rootDiv.getAttribute("class").trim();
  1128. return rootClass.contains(theme);
  1129. }
  1130. }, 30);
  1131. }
  1132. }