View Javadoc

1   package com.atlassian.selenium;
2   
3   import com.thoughtworks.selenium.DefaultSelenium;
4   
5   import java.io.InputStreamReader;
6   import java.io.BufferedReader;
7   import java.io.IOException;
8   import java.util.Map;
9   import java.util.HashMap;
10  
11  /**
12   * Extends the {@link DefaultSelenium} client to provide a more sensible implementation
13   * as well some extra utility methods such as keypress.
14   */
15  public class SeleniumClient extends DefaultSelenium
16  {
17      public enum Browser
18      {
19          FIREFOX("firefox"), OPERA("opera"), SAFARI("safari"), UNKNOWN("unkown"), IE("ie");
20  
21          private final String name;
22  
23          Browser(String name)
24          {
25              this.name = name;
26          }
27  
28          public String getName()
29          {
30              return name;
31          }
32  
33          public static Browser typeOf(String browserStartString)
34          {
35              for (Browser browser : Browser.values())
36              {
37                  if(browserStartString.contains(browser.getName()))
38                  {
39                      return browser;
40                  }
41              }
42              return null;
43          }
44      }
45  
46      private Browser browser;
47  
48      /**
49       * The maximum page load wait time used by Selenium. This value is set with
50       * {@link SeleniumConfiguration#getPageLoadWait()}.
51       */
52      protected final long PAGE_LOAD_WAIT;
53  
54      /**
55       * The maximum wait time for actions that don't require page loads. This value is set with
56       * {@link SeleniumConfiguration#getActionWait()}.
57       */
58      protected final long ACTION_WAIT;
59  
60      public SeleniumClient(SeleniumConfiguration config)
61      {
62          super(new HtmlDumpingHttpCommandProcessor(config.getServerLocation(), config.getServerPort(), config.getBrowserStartString(), config.getBaseUrl()));
63  
64          this.PAGE_LOAD_WAIT = config.getPageLoadWait();
65          this.ACTION_WAIT = config.getActionWait();
66  
67          browser = Browser.typeOf(config.getBrowserStartString());
68  
69          SeleniumStarter.getInstance().setUserAgent(browser.getName());
70      }
71  
72      public Browser getBrowser()
73      {
74          return browser;
75      }
76  
77      /**
78       * Unlike {@link DefaultSelenium#open}, this opens the provided URL relative to the application context path.
79       * It also waits for the page to load -- a maximum of {@link #PAGE_LOAD_WAIT} before returning.
80       */
81      public void open(String url)
82      {
83          open(url, PAGE_LOAD_WAIT);
84      }
85  
86      /**
87       * Wait for page to load doesn't work the case of non-HTML based resources (like images).
88       * So sometimes you really do want to open a url without waiting.
89       * @param url
90       */
91      public void openNoWait(String url)
92      {
93          super.open(url);
94      }
95  
96      /**
97       * Opens the given URL and waits a maximum of timeoutMillis for the page to load completely.
98       */
99      public void open(String url, long timeoutMillis)
100     {
101         super.open(url);
102         super.waitForPageToLoad(String.valueOf(timeoutMillis));
103     }
104 
105     /**
106      * Overloads {@link #waitForPageToLoad(String)} to take in a long.
107      */
108     public void waitForPageToLoad(long timeoutMillis)
109     {
110         super.waitForPageToLoad(String.valueOf(timeoutMillis));
111     }
112 
113     /**
114      * Waits for the page to load with the default timeout configured in {@link SeleniumConfiguration}.
115      */
116     public void waitForPageToLoad()
117     {
118         waitForPageToLoad(PAGE_LOAD_WAIT);
119     }
120 
121     /**
122      * Executes the given Javascript in the context of the text page and waits for it to evaluate to true
123      * for a maximum of {@link #ACTION_WAIT} milliseconds.
124      * @see #waitForCondition(String, long) if you would like to specify your own timeout.
125      */
126     public void waitForCondition(String javascript)
127     {
128         waitForCondition(javascript, ACTION_WAIT);
129     }
130 
131     /**
132      * Executes the given Javascript in the context of the text page and waits for it to evaluate to true
133      * for a maximum of timeoutMillis.
134      */
135     public void waitForCondition(String javascript, long timeoutMillis)
136     {
137         waitForCondition(javascript, Long.toString(timeoutMillis));
138     }
139 
140     /**
141      * Waits for the page to finish loading ajax calls, and returns if there are no more ajax calls currently running.
142      * The method will check for a maximum of {@link #ACTION_WAIT} milliseconds
143      * @see #waitForAjaxWithJquery(long) if you would like to specify your own timeout.
144      */
145     public void waitForAjaxWithJquery()
146     {
147         waitForAjaxWithJquery(ACTION_WAIT);
148     }
149 
150     /**
151      * Waits for the page to finish loading ajax calls, and returns if there are no more ajax calls currently running.
152      * The method will check for a maximum of timeoutMillis
153      */
154     public void waitForAjaxWithJquery(long timeoutMillis)
155     {
156         if (!hasJquery())
157         {
158             throw new UnsupportedOperationException("This operation requires jQuery.");
159         }
160         waitForCondition("selenium.browserbot.getCurrentWindow().jQuery.active == 0;", Long.toString(timeoutMillis));
161     }
162 
163     /**
164      * Click the element with the given locator and optionally wait for the page to load, using {@link #PAGE_LOAD_WAIT}.
165      *
166      * @param locator the element to click, specified using Selenium selector syntax
167      * @param waitForPageToLoad whether to wait for the page to reload. Don't use this unless the page is completely
168      * reloaded.
169      * @see #click(String, long) if you would like to specify your own timeout.
170      */
171     public void click(String locator, boolean waitForPageToLoad)
172     {
173         super.click(locator);
174         if (waitForPageToLoad)
175             super.waitForPageToLoad(String.valueOf(PAGE_LOAD_WAIT));
176     }
177 
178     /**
179      * Submit the named form locator and optionally wait for the page to load, using {@link #PAGE_LOAD_WAIT}.
180      *
181      * @param form to click, specified using Selenium selector syntax
182      * @param waitForPageToLoad whether to wait for the page to reload. Don't use this unless the page is completely
183      * reloaded.
184      * @see #submit(String, long) if you would like to specify your own timeout.
185      */
186     public void submit(String form, boolean waitForPageToLoad)
187     {
188         super.submit(form);
189         if (waitForPageToLoad)
190             super.waitForPageToLoad(String.valueOf(PAGE_LOAD_WAIT));
191     }
192 
193     /**
194      * Click the element with the given locator and wait for the page to load, for a maximum of timeoutMillis.
195      * <p/>
196      * Do not use this method if the page does not reload.
197      *
198      * @param locator the element to click, specified using Selenium selector syntax
199      * @param timeoutMillis the maximum number of milliseconds to wait for the page to load. Polling takes place
200      * more frequently.
201      * @see #click(String, boolean) if you would like to use the default timeout
202      */
203     public void click(String locator, long timeoutMillis)
204     {
205         super.click(locator);
206         super.waitForPageToLoad(Long.toString(timeoutMillis));
207     }
208 
209     /**
210      * Click the element with the given locator and wait for the ajax call to finish.
211      *
212      * @param locator the element to click, specified using Selenium selector syntax
213      */
214     public void clickAndWaitForAjaxWithJquery(String locator)
215     {
216         super.click(locator);
217         waitForAjaxWithJquery();
218     }
219 
220     /**
221      * Click the element with the given locator and wait for the ajax call to finish.
222      *
223      * @param locator the element to click, specified using Selenium selector syntax
224      * @param timeoutMillis the maximum number of milliseconds to wait for the ajax calls to finish.
225      * @see #clickAndWaitForAjaxWithJquery(String) if you would like to use the default timeout
226      */
227     public void clickAndWaitForAjaxWithJquery(String locator, long timeoutMillis)
228     {
229         super.click(locator);
230         waitForAjaxWithJquery(timeoutMillis);
231     }
232 
233     /**
234      * Submit the given form and wait for the page to load, for a maximum of timeoutMillis.
235      * <p/>
236      * Do not use this method if the page does not reload.
237      *
238      * @param form the form to submit
239      * @param timeoutMillis the maximum number of milliseconds to wait for the page to load. Polling takes place
240      * more frequently.
241      * @see #click(String, boolean) if you would like to use the default timeout
242      */
243     public void submit(String form, long timeoutMillis)
244     {
245         super.submit(form);
246         super.waitForPageToLoad(Long.toString(timeoutMillis));
247     }
248 
249     /**
250      * This will type into a field by sending key down / key press / key up events.
251      * @param locator Uses the Selenium locator syntax
252      * @param key The key to be pressed
253      */
254     public void keyPress(String locator, String key)
255     {
256         super.keyDown(locator, key);
257         super.keyPress(locator, key);
258         super.keyUp(locator, key);
259     }
260 
261     /**
262      * This will type into a field by first blanking it out and then sending key down / key press / key up
263      * events.
264      *
265      * @param locator the Selenium locator
266      * @param string  the string to type
267      * @param reset   Should the field be reset first?
268      */
269     public void typeWithFullKeyEvents(String locator, String string, boolean reset)
270     {
271         super.focus(locator);
272         if (reset)
273         {
274             super.type(locator, "");
275         }
276 
277         // The typeKeys method doesn't work properly in Firefox
278         if (Browser.FIREFOX.equals(browser))
279         {
280             char[] chars = string.toCharArray();
281             for (char aChar : chars)
282             {
283                 super.focus(locator);
284                 //Using codes because the methhod doesn't worki n
285                 keyPress(locator, "\\" + (int) aChar);
286             }
287         }
288         else
289         {
290             if(!reset)
291             {
292                 string = super.getValue(locator) + string;
293             }
294             super.type(locator, string);
295             super.typeKeys(locator, string);
296         }
297     }
298 
299     /**
300      * This will type into a field by first blanking it out and then sending key down / key press / key up
301      * events. This really only calls {@link #typeWithFullKeyEvents(String,String,boolean)})}
302      *
303      * @param locator - the usual Selenium locator
304      * @param string  the string to type into a field
305      */
306     public void typeWithFullKeyEvents(String locator, String string)
307     {
308         typeWithFullKeyEvents(locator, string, true);
309     }
310 
311     /**
312      * This will select an option from a {@code select} field.
313      *
314      * @param selectName the select field name
315      * @param label the label to select
316      */
317     public void selectOption(String selectName, String label)
318     {
319         // In some browsers (i.e. Safari) the select items have funny padding
320         // so we need to use this funny method to find how the select item is
321         // padded so that it can be matched
322         String[] options = super.getSelectOptions(selectName);
323         int i = 0;
324         for(; i < options.length; i++)
325         {
326             if(options[i].trim().equals(label))
327             {
328                 break;
329             }
330         }
331         if(i < options.length)
332         {
333             super.select(selectName, options[i]);
334         }
335     }
336 
337     /**
338      * This will select an option from a {@code select} field. If the field calls executes an ajax call onchange of
339      * the value, this method will wait for that ajax method to finish.
340      *
341      * @param selectName the select field name
342      * @param label the label to select
343      */
344     public void selectOptionAndWaitForAjaxWithJquery(String selectName, String label)
345     {
346         this.selectOption(selectName, label);
347         this.waitForAjaxWithJquery();
348     }
349 
350     /**
351      * Checks a checkbox given a name and value.
352      */
353     public void check(String name, String value)
354     {
355         check("name=" + name + " value=" + value);
356     }
357 
358     public void clickLinkWithText(String text, boolean waitForPageToLoad)
359     {
360         super.click("link=" + text);
361         if (waitForPageToLoad) waitForPageToLoad();
362     }
363 
364     public void clickButton(String buttonText, boolean waitForPageToLoad)
365     {
366         clickElementWithXpath("//input[@value = '" + buttonText + "']");
367         if (waitForPageToLoad) waitForPageToLoad();
368     }
369 
370     public void clickButtonAndWaitForAjaxWithJquery(String buttonText)
371     {
372         this.clickButton(buttonText, false);
373         waitForAjaxWithJquery();
374     }
375 
376     public void clickButtonWithName(String buttonName, boolean waitForPageToLoad)
377     {
378         clickElementWithXpath("//input[@name = '" + buttonName + "']");
379         if (waitForPageToLoad) waitForPageToLoad();
380     }
381 
382     public void clickButtonWithNameAndWaitForAjaxWithJquery(String buttonName)
383     {
384         this.clickButtonWithName(buttonName, false);
385         waitForAjaxWithJquery();
386     }
387 
388     public void clickElementWithTitle(String title)
389     {
390         super.click("xpath=//*[@title='" + title + "']");
391     }
392 
393     public void clickElementWithTitleAndWaitForAjaxWithJquery(String title)
394     {
395         this.clickElementWithTitle(title);
396         waitForAjaxWithJquery();
397     }
398 
399     public void clickElementWithClass(String className)
400     {
401         super.click("css=." + className);
402     }
403 
404     public void clickElementWithClassAndWaitForAjaxWithJquery(String className)
405     {
406         this.clickElementWithClass(className);
407         waitForAjaxWithJquery();
408     }
409 
410     public void clickElementWithCss(String cssSelector)
411     {
412         super.click("css=" + cssSelector);
413     }
414 
415     public void clickElementWithCssAndWaitForAjaxWithJquery(String cssSelector)
416     {
417         this.clickElementWithCss(cssSelector);
418         waitForAjaxWithJquery();
419     }
420 
421     public void clickElementWithXpath(String xpath)
422     {
423         super.click("xpath=" + xpath);
424     }
425 
426     public void clickElementWithXpathAndWaitForAjaxWithJquery(String xpath)
427     {
428         this.clickElementWithXpath(xpath);
429         waitForAjaxWithJquery();
430     }
431 
432     public void typeInElementWithName(String elementName, String text)
433     {
434         super.type("name=" + elementName, text);
435     }
436 
437     public void typeInElementWithCss(String cssSelector, String text)
438     {
439         super.type("css=" + cssSelector, text);
440     }
441 
442     public boolean hasJquery()
443     {
444         String evalJquery = getEval("selenium.browserbot.getCurrentWindow().jQuery");
445         return evalJquery != null && !"null".equals(evalJquery) && !"undefined".equals(evalJquery);
446     }
447 
448     private void addJqueryLocator() throws IOException
449     {
450         String jqueryImpl = readFile("jquery-1.3.1.min.js");
451         String jqueryLocStrategy = readFile("jquery-locationStrategy.js");
452         //client.setExtensionJs(jqueryImpl);
453         addScript(jqueryImpl, "jquery");
454         addLocationStrategy("jquery", jqueryLocStrategy );
455     }
456 
457     private static String readFile(String file) throws IOException
458     {
459         BufferedReader reader =  new BufferedReader(new InputStreamReader(ClassLoader.getSystemClassLoader().getResourceAsStream(file)));
460 
461         String line = reader.readLine();
462         StringBuffer contents = new StringBuffer();
463 
464         while(line != null)
465         {
466             contents.append(line + "\n");
467             line = reader.readLine();
468         }
469 
470         return contents.toString();
471     }
472 
473     public void start()
474     {
475         super.start();
476         try {
477             addJqueryLocator();
478         }
479         catch (IOException ioe)
480         {
481             System.err.println("Unable to load JQuery locator strategy: " + ioe);
482             ioe.printStackTrace(System.err);
483         }
484 
485     }
486 }