View Javadoc

1   package com.atlassian.webdriver.visualcomparison;
2   
3   import com.atlassian.selenium.visualcomparison.ScreenElement;
4   import com.atlassian.selenium.visualcomparison.v2.BrowserEngine;
5   import com.atlassian.selenium.visualcomparison.v2.settings.Resolution;
6   import com.atlassian.webdriver.debug.WebDriverDebug;
7   import org.openqa.selenium.*;
8   import org.slf4j.Logger;
9   import org.slf4j.LoggerFactory;
10  
11  import javax.annotation.Nonnull;
12  import javax.annotation.Nullable;
13  import javax.inject.Inject;
14  import java.io.File;
15  import java.util.Collections;
16  import java.util.List;
17  
18  import static com.atlassian.webdriver.utils.JavaScriptUtils.execute;
19  import static com.google.common.base.Preconditions.checkNotNull;
20  import static java.lang.String.format;
21  
22  /**
23   * @since 2.3
24   */
25  public final class WebDriverBrowserEngine implements BrowserEngine
26  {
27      private static final Logger log = LoggerFactory.getLogger(WebDriverBrowserEngine.class);
28  
29      private final WebDriver webDriver;
30  
31      @Inject
32      public WebDriverBrowserEngine(@Nonnull WebDriver webDriver)
33      {
34          this.webDriver = checkNotNull(webDriver, "webDriver");
35      }
36  
37      @SuppressWarnings("ConstantConditions")
38      @Nonnull
39      @Override
40      public ScreenElement getElementAt(int x, int y)
41      {
42          try
43          {
44              ElementByPoint elementByPoint = new ElementByPoint(x, y).calculate();
45              // scroll to put the element in viewport
46              executeScript(Object.class, format("window.scrollTo(%d,%d)",
47                      elementByPoint.scrollTo.getX(), elementByPoint.scrollTo.getY()));
48              final WebElement element = webDriver.findElement(byPoint(elementByPoint.elementCoordinates));
49              return new SimpleScreenElement(executeScript(String.class,
50                      "var d = document.createElement('div'); d.appendChild(arguments[0].cloneNode(true)); return d.innerHTML;",
51                      element));
52          }
53          catch (Exception e)
54          {
55              log.error(format("Error while getting screen element at %d,%d", x, y), e);
56              return new SimpleScreenElement("");
57          }
58      }
59  
60      @Nonnull
61      @Override
62      public BrowserEngine resizeTo(@Nonnull Resolution resolution)
63      {
64          setSize(resolution);
65          // NOT a mistake... WebDriver sometimes needs an extra kick in butt
66          setSize(resolution);
67          return this;
68      }
69  
70      @Nonnull
71      @Override
72      public BrowserEngine captureScreenshotTo(@Nonnull File file)
73      {
74          new WebDriverDebug(webDriver).takeScreenshotTo(file);
75          return this;
76      }
77  
78      @Nonnull
79      @Override
80      public BrowserEngine reloadPage()
81      {
82          webDriver.navigate().refresh();
83          return this;
84      }
85  
86      @Nullable
87      @Override
88      public <T> T executeScript(@Nonnull Class<T> returnType, @Nonnull String script, @Nonnull Object... args)
89      {
90          return execute(returnType, script, webDriver, args);
91      }
92  
93      private By byPoint(final Point coordinates)
94      {
95          return new By()
96          {
97              @Override
98              public List<WebElement> findElements(SearchContext context)
99              {
100                 final String findByPoint = format("return document.elementFromPoint(%d,%d);", coordinates.getX(),
101                         coordinates.getY());
102                 final WebElement element = execute(WebElement.class, findByPoint, webDriver);
103                 return element != null ? Collections.singletonList(element) : Collections.<WebElement>emptyList();
104             }
105 
106             @Override
107             public String toString()
108             {
109                 return format("By point in viewport %d,%d", coordinates.getX(), coordinates.getY());
110             }
111         };
112     }
113 
114     @SuppressWarnings("ConstantConditions")
115     private Dimension getDimensionFor(String selector)
116     {
117         long x = executeScript(Long.class, "return jQuery(" + selector + ").width();");
118         long y = executeScript(Long.class, "return jQuery(" + selector + ").height();");
119         return new Dimension((int) x, (int) y);
120     }
121 
122     private void setSize(Resolution resolution)
123     {
124         webDriver.manage().window().setSize(new Dimension(resolution.getWidth(), resolution.getHeight()));
125     }
126 
127     private final class ElementByPoint
128     {
129         private final int x;
130         private final int y;
131 
132         private Dimension viewport;
133         private Dimension document;
134 
135         Point scrollTo;
136         Point elementCoordinates;
137 
138         private ElementByPoint(int x, int y)
139         {
140             this.x = x;
141             this.y = y;
142         }
143 
144         ElementByPoint calculate()
145         {
146             this.document = getDimensionFor("document");
147             this.viewport = getDimensionFor("window");
148             calculateScroll();
149             calculateCoordinates();
150             return this;
151         }
152 
153         private void calculateScroll()
154         {
155             int deltaX = Math.max(0, document.getWidth() - viewport.getWidth());
156             int scrollX = Math.min(x, deltaX);
157             int deltaY = Math.max(0, document.getHeight() - viewport.getHeight());
158             int scrollY = Math.min(y, deltaY);
159             scrollTo = new Point(scrollX, scrollY);
160         }
161 
162         private void calculateCoordinates()
163         {
164             int relativeX = x - scrollTo.getX();
165             int relativeY = y - scrollTo.getY();
166             elementCoordinates = new Point(relativeX, relativeY);
167         }
168     }
169 
170     private static final class SimpleScreenElement implements ScreenElement
171     {
172 
173         private final String html;
174 
175         private SimpleScreenElement(@Nonnull String html)
176         {
177             this.html = html;
178         }
179 
180         @Override
181         @Nonnull
182         public String getHtml()
183         {
184             return html;
185         }
186     }
187 
188 }