You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

EscalatorSpacerTest.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. package com.vaadin.tests.components.grid.basicfeatures.escalator;
  2. import static org.junit.Assert.assertEquals;
  3. import static org.junit.Assert.assertFalse;
  4. import static org.junit.Assert.assertNotEquals;
  5. import static org.junit.Assert.assertNotNull;
  6. import static org.junit.Assert.assertNull;
  7. import static org.junit.Assert.assertTrue;
  8. import java.util.regex.Matcher;
  9. import java.util.regex.Pattern;
  10. import org.junit.Before;
  11. import org.junit.ComparisonFailure;
  12. import org.junit.Test;
  13. import org.openqa.selenium.By;
  14. import org.openqa.selenium.Keys;
  15. import org.openqa.selenium.WebElement;
  16. import com.vaadin.client.WidgetUtil;
  17. import com.vaadin.shared.Range;
  18. import com.vaadin.testbench.TestBenchElement;
  19. import com.vaadin.testbench.elements.NotificationElement;
  20. import com.vaadin.testbench.parallel.BrowserUtil;
  21. import com.vaadin.tests.components.grid.basicfeatures.EscalatorBasicClientFeaturesTest;
  22. @SuppressWarnings("boxing")
  23. public class EscalatorSpacerTest extends EscalatorBasicClientFeaturesTest {
  24. //@formatter:off
  25. // separate strings made so that eclipse can show the concatenated string by hovering the mouse over the constant
  26. // translate3d(0px, 40px, 123px);
  27. // translate3d(24px, 15.251px, 0);
  28. // translate(0, 40px);
  29. private static final String TRANSLATE_VALUE_REGEX =
  30. "translate(?:3d|)" // "translate" or "translate3d"
  31. + "\\(" // literal "("
  32. + "(" // start capturing the x argument
  33. + "[0-9]+" // the integer part of the value
  34. + "(?:" // start of the subpixel part of the value
  35. + "\\.[0-9]" // if we have a period, there must be at least one number after it
  36. + "[0-9]*" // any amount of accuracy afterwards is fine
  37. + ")?" // the subpixel part is optional
  38. + ")"
  39. + "(?:px)?" // we don't care if the values are suffixed by "px" or not.
  40. + ", "
  41. + "(" // start capturing the y argument
  42. + "[0-9]+" // the integer part of the value
  43. + "(?:" // start of the subpixel part of the value
  44. + "\\.[0-9]" // if we have a period, there must be at least one number after it
  45. + "[0-9]*" // any amount of accuracy afterwards is fine
  46. + ")?" // the subpixel part is optional
  47. + ")"
  48. + "(?:px)?" // we don't care if the values are suffixed by "px" or not.
  49. + "(?:, .*?)?" // the possible z argument, uninteresting (translate doesn't have one, translate3d does)
  50. + "\\)" // literal ")"
  51. + ";?"; // optional ending semicolon
  52. // 40px;
  53. // 12.34px
  54. private static final String PIXEL_VALUE_REGEX =
  55. "(" // capture the pixel value
  56. + "[0-9]+" // the pixel argument
  57. + "(?:" // start of the subpixel part of the value
  58. + "\\.[0-9]" // if we have a period, there must be at least one number after it
  59. + "[0-9]*" // any amount of accuracy afterwards is fine
  60. + ")?" // the subpixel part is optional
  61. + ")"
  62. + "(?:px)?" // optional "px" string
  63. + ";?"; // optional semicolon
  64. //@formatter:on
  65. // also matches "-webkit-transform";
  66. private static final Pattern TRANSFORM_CSS_PATTERN = Pattern
  67. .compile("transform: (.*?);");
  68. private static final Pattern TOP_CSS_PATTERN = Pattern.compile(
  69. "top: ([0-9]+(?:\\.[0-9]+)?(?:px)?);?", Pattern.CASE_INSENSITIVE);
  70. private static final Pattern LEFT_CSS_PATTERN = Pattern.compile(
  71. "left: ([0-9]+(?:\\.[0-9]+)?(?:px)?);?", Pattern.CASE_INSENSITIVE);
  72. private static final Pattern TRANSLATE_VALUE_PATTERN = Pattern
  73. .compile(TRANSLATE_VALUE_REGEX);
  74. private static final Pattern PIXEL_VALUE_PATTERN = Pattern
  75. .compile(PIXEL_VALUE_REGEX, Pattern.CASE_INSENSITIVE);
  76. @Before
  77. public void before() {
  78. setDebug(true);
  79. openTestURL("theme=reindeer");
  80. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, "Set 20px default height");
  81. populate();
  82. }
  83. @Test
  84. public void openVisibleSpacer() {
  85. assertFalse("No spacers should be shown at the start",
  86. spacersAreFoundInDom());
  87. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  88. assertNotNull("Spacer should be shown after setting it", getSpacer(1));
  89. }
  90. @Test
  91. public void closeVisibleSpacer() {
  92. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  93. selectMenuPath(FEATURES, SPACERS, ROW_1, REMOVE);
  94. assertNull("Spacer should not exist after removing it", getSpacer(1));
  95. }
  96. @Test
  97. public void spacerPushesVisibleRowsDown() {
  98. double oldTop = getElementTop(getBodyRow(2));
  99. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  100. double newTop = getElementTop(getBodyRow(2));
  101. assertGreater("Row below a spacer was not pushed down", newTop, oldTop);
  102. }
  103. @Test
  104. public void addingRowAboveSpacerPushesItDown() {
  105. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, REMOVE_ALL_ROWS);
  106. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_BEGINNING);
  107. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_BEGINNING);
  108. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  109. double oldTop = getElementTop(getSpacer(1));
  110. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_BEGINNING);
  111. double newTop = getElementTop(getSpacer(2));
  112. assertGreater("Spacer should've been pushed down (oldTop: " + oldTop
  113. + ", newTop: " + newTop + ")", newTop, oldTop);
  114. }
  115. @Test
  116. public void addingRowBelowSpacerDoesNotPushItDown() {
  117. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, REMOVE_ALL_ROWS);
  118. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_BEGINNING);
  119. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_BEGINNING);
  120. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  121. double oldTop = getElementTop(getSpacer(1));
  122. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_END);
  123. double newTop = getElementTop(getSpacer(1));
  124. assertEquals("Spacer should've not been pushed down", newTop, oldTop,
  125. WidgetUtil.PIXEL_EPSILON);
  126. }
  127. @Test
  128. public void addingRowBelowSpacerIsActuallyRenderedBelowWhenEscalatorIsEmpty() {
  129. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, REMOVE_ALL_ROWS);
  130. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_BEGINNING);
  131. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_BEGINNING);
  132. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  133. double spacerTop = getElementTop(getSpacer(1));
  134. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, ADD_ONE_ROW_TO_END);
  135. double rowTop = getElementTop(getBodyRow(2));
  136. assertEquals("Next row should've been rendered below the spacer",
  137. spacerTop + 100, rowTop, WidgetUtil.PIXEL_EPSILON);
  138. }
  139. @Test
  140. public void addSpacerAtBottomThenScrollThere() {
  141. selectMenuPath(FEATURES, SPACERS, ROW_99, SET_100PX);
  142. scrollVerticallyTo(999999);
  143. assertFalse("Did not expect a notification",
  144. $(NotificationElement.class).exists());
  145. }
  146. @Test
  147. public void scrollToBottomThenAddSpacerThere() {
  148. scrollVerticallyTo(999999);
  149. long oldBottomScrollTop = getScrollTop();
  150. selectMenuPath(FEATURES, SPACERS, ROW_99, SET_100PX);
  151. assertEquals(
  152. "Adding a spacer underneath the current viewport should "
  153. + "not scroll anywhere",
  154. oldBottomScrollTop, getScrollTop());
  155. assertFalse("Got an unexpected notification",
  156. $(NotificationElement.class).exists());
  157. scrollVerticallyTo(999999);
  158. assertFalse("Got an unexpected notification",
  159. $(NotificationElement.class).exists());
  160. assertGreater("Adding a spacer should've made the scrollbar scroll "
  161. + "further", getScrollTop(), oldBottomScrollTop);
  162. }
  163. @Test
  164. public void removingRowAboveSpacerMovesSpacerUp() {
  165. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  166. WebElement spacer = getSpacer(1);
  167. double originalElementTop = getElementTop(spacer);
  168. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS,
  169. REMOVE_ONE_ROW_FROM_BEGINNING);
  170. assertLessThan("spacer should've moved up", getElementTop(spacer),
  171. originalElementTop);
  172. assertNull("No spacer for row 1 should be found after removing the "
  173. + "top row", getSpacer(1));
  174. }
  175. @Test
  176. public void removingSpacedRowRemovesSpacer() {
  177. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  178. assertTrue("Spacer should've been found in the DOM",
  179. spacersAreFoundInDom());
  180. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS,
  181. REMOVE_ONE_ROW_FROM_BEGINNING);
  182. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS,
  183. REMOVE_ONE_ROW_FROM_BEGINNING);
  184. assertFalse("No spacers should be in the DOM after removing "
  185. + "associated spacer", spacersAreFoundInDom());
  186. }
  187. @Test
  188. public void spacersAreFixedInViewport_firstFreezeThenScroll() {
  189. selectMenuPath(FEATURES, FROZEN_COLUMNS, FREEZE_1_COLUMN);
  190. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  191. assertEquals(
  192. "Spacer's left position should've been 0 at the " + "beginning",
  193. 0d, getElementLeft(getSpacer(1)), WidgetUtil.PIXEL_EPSILON);
  194. int scrollTo = 10;
  195. scrollHorizontallyTo(scrollTo);
  196. assertEquals(
  197. "Spacer's left position should've been " + scrollTo
  198. + " after scrolling " + scrollTo + "px",
  199. scrollTo, getElementLeft(getSpacer(1)),
  200. WidgetUtil.PIXEL_EPSILON);
  201. }
  202. @Test
  203. public void spacersAreFixedInViewport_firstScrollThenFreeze() {
  204. selectMenuPath(FEATURES, FROZEN_COLUMNS, FREEZE_1_COLUMN);
  205. int scrollTo = 10;
  206. scrollHorizontallyTo(scrollTo);
  207. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  208. assertEquals(
  209. "Spacer's left position should've been " + scrollTo
  210. + " after scrolling " + scrollTo + "px",
  211. scrollTo, getElementLeft(getSpacer(1)),
  212. WidgetUtil.PIXEL_EPSILON);
  213. }
  214. @Test
  215. public void addingMinusOneSpacerDoesNotScrollWhenScrolledAtTop() {
  216. scrollVerticallyTo(5);
  217. selectMenuPath(FEATURES, SPACERS, ROW_MINUS1, SET_100PX);
  218. assertEquals(
  219. "No scroll adjustment should've happened when adding the -1 spacer",
  220. 5, getScrollTop());
  221. }
  222. @Test
  223. public void removingMinusOneSpacerScrolls() {
  224. scrollVerticallyTo(5);
  225. selectMenuPath(FEATURES, SPACERS, ROW_MINUS1, SET_100PX);
  226. selectMenuPath(FEATURES, SPACERS, ROW_MINUS1, REMOVE);
  227. assertEquals("Scroll adjustment should've happened when removing the "
  228. + "-1 spacer", 0, getScrollTop());
  229. }
  230. @Test
  231. public void scrollToRowWorksProperlyWithSpacers() throws Exception {
  232. selectMenuPath(FEATURES, SPACERS, ROW_MINUS1, SET_100PX);
  233. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  234. /*
  235. * we check for row -2 instead of -1, because escalator has one row
  236. * buffered underneath the footer
  237. */
  238. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, SCROLL_TO, ROW_75);
  239. Thread.sleep(500);
  240. TestBenchElement cell75 = getBodyCell(-2, 0);
  241. assertEquals("Row 75: 0,75", cell75.getText());
  242. // confirm the scroll position
  243. WebElement footer = findElement(By.className("v-escalator-footer"));
  244. assertEquals(footer.getLocation().y,
  245. cell75.getLocation().y + cell75.getSize().height);
  246. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, SCROLL_TO, ROW_25);
  247. Thread.sleep(500);
  248. try {
  249. assertEquals("Row 25: 0,25", getBodyCell(0, 0).getText());
  250. } catch (ComparisonFailure retryForIE10andIE11) {
  251. /*
  252. * This seems to be some kind of subpixel/off-by-one-pixel error.
  253. * Everything's scrolled correctly, but Escalator still loads one
  254. * row above to the DOM, underneath the header. It's there, but it's
  255. * not visible. We'll allow for that one pixel error.
  256. */
  257. assertEquals("Row 24: 0,24", getBodyCell(0, 0).getText());
  258. }
  259. }
  260. @Test
  261. public void scrollToSpacerFromAbove() throws Exception {
  262. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  263. selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
  264. // Browsers might vary with a few pixels.
  265. Range allowableScrollRange = Range.between(765, 780);
  266. int scrollTop = (int) getScrollTop();
  267. assertTrue("Scroll position was not " + allowableScrollRange + ", but "
  268. + scrollTop, allowableScrollRange.contains(scrollTop));
  269. }
  270. @Test
  271. public void scrollToSpacerFromBelow() throws Exception {
  272. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  273. scrollVerticallyTo(999999);
  274. selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
  275. // Browsers might vary with a few pixels.
  276. Range allowableScrollRange = Range.between(1015, 1025);
  277. int scrollTop = (int) getScrollTop();
  278. assertTrue("Scroll position was not " + allowableScrollRange + ", but "
  279. + scrollTop, allowableScrollRange.contains(scrollTop));
  280. }
  281. @Test
  282. public void scrollToSpacerAlreadyInViewport() throws Exception {
  283. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  284. scrollVerticallyTo(1000);
  285. selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
  286. assertEquals(getScrollTop(), 1000);
  287. }
  288. @Test
  289. public void scrollToRowAndSpacerFromAbove() throws Exception {
  290. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  291. selectMenuPath(FEATURES, SPACERS, ROW_50,
  292. SCROLL_HERE_SPACERBELOW_ANY_0PADDING);
  293. // Browsers might vary with a few pixels.
  294. Range allowableScrollRange = Range.between(765, 780);
  295. int scrollTop = (int) getScrollTop();
  296. assertTrue("Scroll position was not " + allowableScrollRange + ", but "
  297. + scrollTop, allowableScrollRange.contains(scrollTop));
  298. }
  299. @Test
  300. public void scrollToRowAndSpacerFromBelow() throws Exception {
  301. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  302. scrollVerticallyTo(999999);
  303. selectMenuPath(FEATURES, SPACERS, ROW_50,
  304. SCROLL_HERE_SPACERBELOW_ANY_0PADDING);
  305. // Browsers might vary with a few pixels.
  306. Range allowableScrollRange = Range.between(995, 1005);
  307. int scrollTop = (int) getScrollTop();
  308. assertTrue("Scroll position was not " + allowableScrollRange + ", but "
  309. + scrollTop, allowableScrollRange.contains(scrollTop));
  310. }
  311. @Test
  312. public void scrollToRowAndSpacerAlreadyInViewport() throws Exception {
  313. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  314. scrollVerticallyTo(950);
  315. selectMenuPath(FEATURES, SPACERS, ROW_50,
  316. SCROLL_HERE_SPACERBELOW_ANY_0PADDING);
  317. assertEquals(getScrollTop(), 950);
  318. }
  319. @Test
  320. public void domCanBeSortedWithFocusInSpacer() throws InterruptedException {
  321. // Firefox behaves badly with focus-related tests - skip it.
  322. if (BrowserUtil.isFirefox(super.getDesiredCapabilities())) {
  323. return;
  324. }
  325. selectMenuPath(FEATURES, SPACERS, FOCUSABLE_UPDATER);
  326. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  327. WebElement inputElement = getEscalator()
  328. .findElement(By.tagName("input"));
  329. inputElement.click();
  330. scrollVerticallyTo(30);
  331. // Sleep needed because of all the JS we're doing, and to let
  332. // the DOM reordering to take place.
  333. Thread.sleep(500);
  334. assertFalse("Error message detected",
  335. $(NotificationElement.class).exists());
  336. }
  337. @Test
  338. public void spacersAreInsertedInCorrectDomPosition() {
  339. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  340. WebElement tbody = getEscalator().findElement(By.tagName("tbody"));
  341. WebElement spacer = getChild(tbody, 2);
  342. String cssClass = spacer.getAttribute("class");
  343. assertTrue(
  344. "element index 2 was not a spacer (class=\"" + cssClass + "\")",
  345. cssClass.contains("-spacer"));
  346. }
  347. @Test
  348. public void spacersAreInCorrectDomPositionAfterScroll()
  349. throws InterruptedException {
  350. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  351. scrollVerticallyTo(40); // roughly two rows' worth
  352. // both rows should still be within DOM after this little scrolling, so
  353. // the spacer should be the third element within the body (index: 2)
  354. WebElement tbody = getEscalator().findElement(By.tagName("tbody"));
  355. WebElement spacer = getChild(tbody, 2);
  356. String cssClass = spacer.getAttribute("class");
  357. assertTrue(
  358. "element index 2 was not a spacer (class=\"" + cssClass + "\")",
  359. cssClass.contains("-spacer"));
  360. // Scroll to last DOM row (Row 20). The exact position varies a bit
  361. // depending on the browser.
  362. int scrollTo = 172;
  363. while (scrollTo < 176) {
  364. scrollVerticallyTo(scrollTo);
  365. Thread.sleep(500);
  366. // if spacer is still the third (index: 2) body element, i.e. not
  367. // enough scrolling to re-purpose any rows, scroll a bit further
  368. spacer = getChild(tbody, 2);
  369. cssClass = spacer.getAttribute("class");
  370. if (cssClass.contains("-spacer")) {
  371. ++scrollTo;
  372. } else {
  373. break;
  374. }
  375. }
  376. if (getChild(tbody, 20).getText().startsWith("Row 22:")) {
  377. // Some browsers scroll too much, spacer should be out of visual
  378. // range
  379. assertNull("Element found where there should be none",
  380. getChild(tbody, 21));
  381. } else {
  382. // second row should still be within DOM but the first row out of
  383. // it, so the spacer should be the second element within the body
  384. // (index: 1)
  385. spacer = getChild(tbody, 1);
  386. cssClass = spacer.getAttribute("class");
  387. assertTrue("element index 1 was not a spacer (class=\"" + cssClass
  388. + "\")", cssClass.contains("-spacer"));
  389. }
  390. }
  391. @Test
  392. public void spacerScrolledIntoViewGetsFocus() {
  393. selectMenuPath(FEATURES, SPACERS, FOCUSABLE_UPDATER);
  394. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  395. selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
  396. tryToTabIntoFocusUpdaterElement();
  397. assertEquals("input", getFocusedElement().getTagName());
  398. }
  399. @Test
  400. public void spacerScrolledOutOfViewDoesNotGetFocus() {
  401. selectMenuPath(FEATURES, SPACERS, FOCUSABLE_UPDATER);
  402. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  403. selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
  404. tryToTabIntoFocusUpdaterElement();
  405. assertNotEquals("input", getFocusedElement().getTagName());
  406. }
  407. @Test
  408. public void spacerOpenedInViewGetsFocus() {
  409. selectMenuPath(FEATURES, SPACERS, FOCUSABLE_UPDATER);
  410. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  411. tryToTabIntoFocusUpdaterElement();
  412. WebElement focusedElement = getFocusedElement();
  413. assertEquals("input", focusedElement.getTagName());
  414. }
  415. @Test
  416. public void spacerOpenedOutOfViewDoesNotGetFocus() {
  417. selectMenuPath(FEATURES, SPACERS, FOCUSABLE_UPDATER);
  418. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  419. tryToTabIntoFocusUpdaterElement();
  420. assertNotEquals("input", getFocusedElement().getTagName());
  421. }
  422. @Test
  423. public void spacerOpenedInViewAndScrolledOutAndBackAgainGetsFocus() {
  424. selectMenuPath(FEATURES, SPACERS, FOCUSABLE_UPDATER);
  425. selectMenuPath(FEATURES, SPACERS, ROW_1, SET_100PX);
  426. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, SCROLL_TO, ROW_50);
  427. selectMenuPath(FEATURES, SPACERS, ROW_1, SCROLL_HERE_ANY_0PADDING);
  428. tryToTabIntoFocusUpdaterElement();
  429. assertEquals("input", getFocusedElement().getTagName());
  430. }
  431. @Test
  432. public void spacerOpenedOutOfViewAndScrolledInAndBackAgainDoesNotGetFocus() {
  433. selectMenuPath(FEATURES, SPACERS, FOCUSABLE_UPDATER);
  434. selectMenuPath(FEATURES, SPACERS, ROW_50, SET_100PX);
  435. selectMenuPath(FEATURES, SPACERS, ROW_50, SCROLL_HERE_ANY_0PADDING);
  436. selectMenuPath(COLUMNS_AND_ROWS, BODY_ROWS, SCROLL_TO, ROW_0);
  437. tryToTabIntoFocusUpdaterElement();
  438. assertNotEquals("input", getFocusedElement().getTagName());
  439. }
  440. private void tryToTabIntoFocusUpdaterElement() {
  441. ((TestBenchElement) findElement(By.className("gwt-MenuBar"))).focus();
  442. WebElement focusedElement = getFocusedElement();
  443. focusedElement.sendKeys(Keys.TAB);
  444. }
  445. private WebElement getChild(WebElement parent, int childIndex) {
  446. return (WebElement) executeScript(
  447. "return arguments[0].children[" + childIndex + "];", parent);
  448. }
  449. private static double[] getElementDimensions(WebElement element) {
  450. /*
  451. * we need to parse the style attribute, since using getCssValue gets a
  452. * normalized value that is harder to parse.
  453. */
  454. String style = element.getAttribute("style");
  455. String transform = getTransformFromStyle(style);
  456. if (transform != null) {
  457. return getTranslateValues(transform);
  458. }
  459. double[] result = { -1, -1 };
  460. String left = getLeftFromStyle(style);
  461. if (left != null) {
  462. result[0] = getPixelValue(left);
  463. }
  464. String top = getTopFromStyle(style);
  465. if (top != null) {
  466. result[1] = getPixelValue(top);
  467. }
  468. if (result[0] != -1 && result[1] != -1) {
  469. return result;
  470. } else {
  471. throw new IllegalArgumentException("Could not parse the position "
  472. + "information from the CSS \"" + style + "\"");
  473. }
  474. }
  475. private static double getElementTop(WebElement element) {
  476. return getElementDimensions(element)[1];
  477. }
  478. private static double getElementLeft(WebElement element) {
  479. return getElementDimensions(element)[0];
  480. }
  481. private static String getTransformFromStyle(String style) {
  482. return getFromStyle(TRANSFORM_CSS_PATTERN, style);
  483. }
  484. private static String getTopFromStyle(String style) {
  485. return getFromStyle(TOP_CSS_PATTERN, style);
  486. }
  487. private static String getLeftFromStyle(String style) {
  488. return getFromStyle(LEFT_CSS_PATTERN, style);
  489. }
  490. private static String getFromStyle(Pattern pattern, String style) {
  491. Matcher matcher = pattern.matcher(style);
  492. if (matcher.find()) {
  493. assertEquals("wrong amount of groups matched in " + style, 1,
  494. matcher.groupCount());
  495. return matcher.group(1);
  496. } else {
  497. return null;
  498. }
  499. }
  500. /**
  501. * @return {@code [0] == x}, {@code [1] == y}
  502. */
  503. private static double[] getTranslateValues(String translate) {
  504. Matcher matcher = TRANSLATE_VALUE_PATTERN.matcher(translate);
  505. assertTrue("no matches for " + translate + " against "
  506. + TRANSLATE_VALUE_PATTERN, matcher.find());
  507. assertEquals("wrong amout of groups matched in " + translate, 2,
  508. matcher.groupCount());
  509. return new double[] { Double.parseDouble(matcher.group(1)),
  510. Double.parseDouble(matcher.group(2)) };
  511. }
  512. private static double getPixelValue(String top) {
  513. Matcher matcher = PIXEL_VALUE_PATTERN.matcher(top);
  514. assertTrue(
  515. "no matches for \"" + top + "\" against " + PIXEL_VALUE_PATTERN,
  516. matcher.find());
  517. assertEquals("wrong amount of groups matched in " + top, 1,
  518. matcher.groupCount());
  519. return Double.parseDouble(matcher.group(1));
  520. }
  521. }