選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

AbstractTB3Test.java 43KB

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