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