View Javadoc

1   package com.atlassian.pageobjects.elements;
2   
3   import com.atlassian.pageobjects.PageBinder;
4   import com.atlassian.pageobjects.elements.query.AbstractTimedQuery;
5   import com.atlassian.pageobjects.elements.query.ExpirationHandler;
6   import com.atlassian.pageobjects.elements.query.TimedQuery;
7   import com.atlassian.webdriver.utils.JavaScriptUtils;
8   import com.google.common.collect.ImmutableSet;
9   import com.google.common.collect.Iterables;
10  import org.apache.commons.lang.ArrayUtils;
11  import org.openqa.selenium.JavascriptExecutor;
12  import org.openqa.selenium.WebDriver;
13  import org.openqa.selenium.WebElement;
14  
15  import java.util.List;
16  
17  import static com.atlassian.pageobjects.elements.WebDriverLocators.staticElement;
18  import static com.google.common.base.Preconditions.checkNotNull;
19  import static com.google.common.base.Preconditions.checkState;
20  
21  /**
22   * Executes javascript on an element
23   */
24  final class WebDriverElementJavascript implements PageElementJavascript
25  {
26      private static final Iterable<String> SIMPLE_JS_RESULT_TYPES = ImmutableSet.of(
27              Boolean.class.getName(),
28              String.class.getName(),
29              Long.class.getName(),
30              List.class.getName()
31      );
32  
33  
34      private final WebDriver driver;
35      private final PageBinder pageBinder;
36      private final WebDriverElement element;
37  
38      private final JSMouse jsMouse = new JSMouse();
39      private final JSForm jsForm = new JSForm();
40  
41  
42  
43      WebDriverElementJavascript(WebDriverElement element)
44      {
45          this.element = checkNotNull(element, "element");
46          this.driver = element.driver;
47          this.pageBinder = element.pageBinder;
48      }
49  
50      public PageElementMouseJavascript mouse()
51      {
52          return jsMouse;
53      }
54  
55      public PageElementFormJavascript form()
56      {
57          return jsForm;
58      }
59  
60  
61      public Object execute(String script, Object... arguments)
62      {
63          checkNotNull(script, "script");
64          return convertResult(getExecutor().executeScript(script, convertArguments(arguments)));
65      }
66  
67      public <T> T execute(Class<T> resultType, String script, Object... arguments)
68      {
69          checkNotNull(script, "script");
70          validateResultType(resultType);
71          return executeConverted(resultType, script, arguments);
72      }
73  
74      private <T> T executeConverted(Class<T> resultType, String script, Object[] arguments)
75      {
76          return convertResult(getExecutor().executeScript(script, convertArguments(arguments)), resultType);
77      }
78  
79      @Override
80      public <T> TimedQuery<T> executeTimed(final Class<T> resultType, final String script, final Object... arguments)
81      {
82          checkNotNull(script, "script");
83          validateSimpleResultType(resultType);
84          return new AbstractTimedQuery<T>(element.timed().isPresent(), ExpirationHandler.RETURN_CURRENT)
85          {
86              @Override
87              protected boolean shouldReturn(T currentEval)
88              {
89                  return true;
90              }
91  
92              @Override
93              protected T currentValue()
94              {
95                  return executeConverted(resultType, script, arguments);
96              }
97          };
98      }
99  
100     @Override
101     public <T> T executeAsync(Class<T> resultType, String script, Object... arguments)
102     {
103         checkNotNull(script, "script");
104         validateResultType(resultType);
105         return executeAsyncConverted(resultType, script, arguments);
106     }
107 
108     public Object executeAsync(String script, Object... arguments)
109     {
110         checkNotNull(script, "script");
111         return convertResult(getExecutor().executeAsyncScript(script, convertArguments(arguments)));
112     }
113 
114     private <T> T executeAsyncConverted(Class<T> resultType, String script, Object[] arguments)
115     {
116         return convertResult(getExecutor().executeAsyncScript(script, convertArguments(arguments)), resultType);
117     }
118 
119     private Object[] convertArguments(Object[] arguments)
120     {
121         for (int i=0; i<arguments.length;i++)
122         {
123             if (arguments[i] instanceof PageElement)
124             {
125                 arguments[i] = WebDriverElement.getWebElement((PageElement) arguments[i]);
126             }
127         }
128         return ArrayUtils.add(arguments, 0, element.waitForWebElement());
129     }
130 
131     private Object convertResult(Object result)
132     {
133         if (result instanceof WebElement)
134         {
135             final WebElement webElement = (WebElement) result;
136             if (element.getAttribute("id") != null && element.getAttribute("id").equals(webElement.getAttribute("id")))
137             {
138                 // result same as owning element
139                 return element;
140             }
141             return pageBinder.bind(WebDriverElement.class, staticElement(webElement), element.defaultTimeout);
142         }
143         else
144         {
145             return result;
146         }
147     }
148 
149     private <T> T convertResult(Object result, Class<T> expected)
150     {
151         if (result instanceof WebElement)
152         {
153             if (!PageElement.class.isAssignableFrom(expected))
154             {
155                 throw new ClassCastException("Expected instance of PageElement, was: " + expected);
156             }
157             final WebElement webElement = (WebElement) result;
158             if (element.getAttribute("id") != null && element.getAttribute("id").equals(webElement.getAttribute("id")))
159             {
160                 // result same as owning element
161                 return expected.cast(element);
162             }
163             @SuppressWarnings("unchecked") final Object stupidWorkaroundsForStupidGenericsProblemsHaaaaaateIt =
164                     pageBinder.bind(WebDriverElementMappings.findMapping((Class)expected), staticElement(webElement), element.defaultTimeout);
165             return expected.cast(stupidWorkaroundsForStupidGenericsProblemsHaaaaaateIt);
166         }
167         else
168         {
169             return expected.cast(result);
170         }
171     }
172 
173     private JavascriptExecutor getExecutor()
174     {
175         checkState(driver instanceof JavascriptExecutor, driver + " does not support Javascript");
176         return (JavascriptExecutor) driver;
177     }
178 
179     private <T> void validateSimpleResultType(Class<T> resultType)
180     {
181         checkNotNull(resultType, "resultType");
182         if (!Iterables.contains(SIMPLE_JS_RESULT_TYPES, resultType.getName()))
183         {
184             throw new IllegalArgumentException("Class '" + resultType.getName() + "' is not a simple JS return type");
185         }
186     }
187 
188     private <T> void validateResultType(Class<T> resultType)
189     {
190         if (Iterables.contains(SIMPLE_JS_RESULT_TYPES, resultType.getName()))
191         {
192             return;
193         }
194         if (PageElement.class.isAssignableFrom(resultType))
195         {
196             return;
197         }
198         throw new IllegalArgumentException("Class '" + resultType.getName() + "' is not a supported JS return type");
199     }
200 
201     private class JSMouse implements PageElementMouseJavascript
202     {
203 
204         public PageElementJavascript click()
205         {
206             return dispatch("click");
207         }
208 
209         public PageElementJavascript doubleClick()
210         {
211             return dispatch("dblclick");
212         }
213 
214         public PageElementJavascript mouseup()
215         {
216             return dispatch("mouseup");
217         }
218 
219         public PageElementJavascript mousedown()
220         {
221             return dispatch("mousedown");
222         }
223 
224         public PageElementJavascript mouseover()
225         {
226             return dispatch("mouseover");
227         }
228 
229         public PageElementJavascript mousemove()
230         {
231             return dispatch("mousemove");
232         }
233 
234         public PageElementJavascript mouseout()
235         {
236             return dispatch("mouseout");
237         }
238 
239     }
240 
241 
242     private class JSForm implements PageElementFormJavascript
243     {
244 
245         public PageElementJavascript select()
246         {
247             return dispatch("select");
248         }
249 
250         public PageElementJavascript change()
251         {
252             return dispatch("change");
253         }
254 
255         public PageElementJavascript submit()
256         {
257             return dispatch("submit");
258         }
259 
260         public PageElementJavascript focus()
261         {
262             return dispatch("focus");
263         }
264 
265         public PageElementJavascript blur()
266         {
267             return dispatch("blur");
268         }
269     }
270 
271     private PageElementJavascript dispatch(String eventType)
272     {
273         JavaScriptUtils.dispatchEvent(eventType, element.waitForWebElement(), driver);
274         return this;
275     }
276 }