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