View Javadoc

1   /*
2       Jameleon HtmlUnit plug-in - A plug-in that uses HtmlUnit to drive web sites
3       Copyright (C) 2006 Christian W. Hargraves (engrean@hotmail.com)
4       
5       This library is free software; you can redistribute it and/or
6       modify it under the terms of the GNU Lesser General Public
7       License as published by the Free Software Foundation; either
8       version 2.1 of the License, or (at your option) any later version.
9   
10      This library is distributed in the hope that it will be useful,
11      but WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13      Lesser General Public License for more details.
14  
15      You should have received a copy of the GNU Lesser General Public
16      License along with this library; if not, write to the Free Software
17      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111AssertLevel.NO_FUNCTION07 USA
18  */
19  package net.sf.jameleon.plugin.htmlunit.util;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.util.List;
26  
27  import net.sf.jameleon.util.JameleonUtility;
28  
29  import org.jaxen.JaxenException;
30  import org.jaxen.Navigator;
31  
32  import com.gargoylesoftware.htmlunit.ElementNotFoundException;
33  import com.gargoylesoftware.htmlunit.Page;
34  import com.gargoylesoftware.htmlunit.WebClient;
35  import com.gargoylesoftware.htmlunit.WebResponse;
36  import com.gargoylesoftware.htmlunit.WebWindow;
37  import com.gargoylesoftware.htmlunit.html.ClickableElement;
38  import com.gargoylesoftware.htmlunit.html.DomNode;
39  import com.gargoylesoftware.htmlunit.html.HtmlCheckBoxInput;
40  import com.gargoylesoftware.htmlunit.html.HtmlElement;
41  import com.gargoylesoftware.htmlunit.html.HtmlForm;
42  import com.gargoylesoftware.htmlunit.html.HtmlInput;
43  import com.gargoylesoftware.htmlunit.html.HtmlOption;
44  import com.gargoylesoftware.htmlunit.html.HtmlPage;
45  import com.gargoylesoftware.htmlunit.html.HtmlRadioButtonInput;
46  import com.gargoylesoftware.htmlunit.html.HtmlSelect;
47  import com.gargoylesoftware.htmlunit.html.HtmlTextArea;
48  import com.gargoylesoftware.htmlunit.html.xpath.HtmlUnitXPath;
49  
50  /***
51   * This class is used as a facade around the HtmlUnit API and is used by both
52   * the HtmlUnitSessionTag and the HtmlUnitFunctionTag.
53   */
54  public class HtmlUnitHelper {
55  
56      protected HtmlUnitDelegate delegate;
57  
58      public HtmlUnitHelper(HtmlUnitDelegate delegate) {
59          this.delegate = delegate;
60      }
61  
62      /***
63       * Clicks on an HtmlElement (ClickableElement) defined by the provided XPath expression
64       * @param xpath - The XPath expression to locate the element to click on.
65       */
66      public void clickElementWithXPath(String xpath) {
67          HtmlElement element = getHtmlElementByXPath(xpath);
68          if ( element != null && element instanceof ClickableElement ) {
69              try {
70                  ((ClickableElement)element).click();
71              } catch ( IOException ioe ) {
72                  throw new RuntimeException(ioe.getMessage(), ioe);
73              }
74          } else {
75              String errMsg;
76              if ( element == null ) {
77                  errMsg = "No element found to click on that matches the given XPath '"+xpath+"'";
78              } else {
79                  errMsg = "The element returned ("+element.getTagName()+") can not be clicked on";
80              }
81              throw new RuntimeException(errMsg);
82          }
83      }
84  
85      /***
86       * Gets the HtmlUnitDelegate that this object uses to communicate with the
87       * existing session.
88       * 
89       * @return The HtmlUnitDelegate that this object uses to communicate with the
90       * existing session.
91       */
92      public HtmlUnitDelegate getDelegate() {
93          return delegate;
94      }
95  
96      /***
97       * Gets the current page in the currently active WebWindow
98       * 
99       * @return The current page in the currently active WebWindow
100      */
101     public Page getCurrentPage() {
102         Page page = null;
103         WebWindow window = delegate.getCurrentWebWindow();
104         if ( window != null ) {
105             page = window.getEnclosedPage();
106         }
107         return page;
108     }
109 
110     /***
111      * Gets the HTML source from the currently active window
112      * 
113      * @return The HTML source used in the current window
114      */
115     public String getCurrentPageContent() {
116         String content = null;
117         WebResponse res = getCurrentWebResponse();
118         if ( res != null ) {
119             content = res.getContentAsString();
120         }
121         return content;
122     }
123 
124     /***
125      * Gets the currnet WebResponse
126      * 
127      * @return The current WebResponse
128      */
129     public WebResponse getCurrentWebResponse() {
130         WebResponse res = null;
131         Page page = getCurrentPage();
132         if ( page != null ) {
133             res = page.getWebResponse();
134         }
135         return res;
136     }
137 
138     /***
139      * Gets an HtmlElement matching the provided xpath expression
140      * @param xpath - An XPath expression that matches the desired HtmlElement
141      * 
142      * @return The matching HtmlElement
143      */
144     public HtmlElement getHtmlElementByXPath(String xpath) {
145         HtmlElement element = null;
146         if ( xpath != null ) {
147             element = getHtmlElementByXPath((HtmlPage)getCurrentPage(), xpath);
148         } else {
149             throw new RuntimeException("xpath was null! Supply an xpath expression.");
150         }
151         return element;
152     }
153 
154     /***
155      * Gets an HtmlElement matching the provided xpath expression
156      * @param container - The DOM to start at
157      * @param xpath - An XPath expression that matches the desired HtmlElement
158      * 
159      * @return The matching HtmlElement
160      */
161     public HtmlElement getHtmlElementByXPath(DomNode container, String xpath) {
162         HtmlElement element = null;
163         if ( xpath != null && container != null ) {
164             Navigator nav = HtmlUnitXPath.buildSubtreeNavigator(container);
165             try {
166                 HtmlUnitXPath xp = new HtmlUnitXPath(xpath, nav);
167                 element = (HtmlElement)xp.selectSingleNode(getCurrentPage());
168             } catch ( JaxenException je ) {
169                 throw new RuntimeException("Problem processing XPath '"+xpath+"'", je);
170             }
171         } else {
172             throw new RuntimeException("Neither xpath nor container can be null! Supply an xpath expression and the container to execute it in.");
173         }
174         return element;
175     }
176 
177     /***
178      * Gets an HTMLElement by the tag name, attribute name and the attribute value.
179      * This then in turn gets translated to an XPath expression.
180      * An example of this might be to get a form with its id='ten'.
181      * @param tagname - The name of the tag to get back. In the above example, the value would be 'form'
182      * @param attributeName - The name of attribute to check against. In the above example, the value would be 'id'
183      * @param attributeValue - The value of the attribute to check against. In the above example, the value would be 'ten'
184      * 
185      * @return HtmlElement - An element that matches the criteria.
186      */
187     public HtmlElement getHtmlElementByAttributeNameAndValue(String tagname, String attributeName, String attributeValue) {
188         HtmlElement element = null;
189         if ( tagname != null && attributeName != null && attributeValue != null ) {
190             String xpath = "//"+tagname+"[@"+attributeName+"='"+attributeValue+"']";
191             element = getHtmlElementByXPath(xpath);
192         } else {
193             throw new RuntimeException("The 'tagname', 'attributeName' and 'attributeValue' parameters must all be set!");
194         }
195         return element;
196     }
197 
198     /***
199      * Gets a form element back by its id attribute.
200      * @param id - the value of the id attribute.
201      * 
202      * @return HtmlForm
203      */
204     public HtmlForm getHtmlFormById(String id) {
205         HtmlForm form = null;
206         if ( id != null ) {
207             HtmlPage page = (HtmlPage)getCurrentPage();
208             HtmlElement elmnt = null;
209             try {
210                 elmnt = page.getHtmlElementById(id);
211                 if ( elmnt instanceof HtmlForm ) {
212                     form = (HtmlForm) elmnt;
213                 }
214             } catch ( ElementNotFoundException enfe ) {
215                 //Do nothing. We will instead return null
216             }
217         } else {
218             throw new RuntimeException("The 'id' parameter was null!");
219         }
220         return form;
221     }
222 
223     /***
224      * Gets a form element back by its index or location on the page.
225      * For the first form on the page, pass in '1'
226      * @param index - the nth form on the page
227      * 
228      * @return HtmlForm
229      */
230     public HtmlForm getHtmlFormByIndex(int index) {
231         return(HtmlForm)getHtmlElementByXPath("//form["+index+"]");
232     }
233 
234     /***
235      * Gets a form element back by its name attribute.
236      * @param formName - the value of the name attribute.
237      * 
238      * @return HtmlForm
239      */
240     public HtmlForm getHtmlFormByName(String formName) {
241         HtmlForm form = null;
242         if ( formName != null ) {
243             HtmlPage page = (HtmlPage)getCurrentPage();
244             try {
245                 form = page.getFormByName(formName);
246             } catch ( ElementNotFoundException enfe ) {
247                 //Do nothing here. We will return null instead of throwing an exception
248             }
249         } else {
250             throw new RuntimeException("The 'formName' parameter was null!");
251         }
252         return form;
253     }
254 
255     /***
256      * Gets a form element back by an XPath expression
257      * @param xpath - The XPath expression matching the desired form
258      * 
259      * @return HtmlForm
260      */
261     public HtmlForm getHtmlFormByXPath(String xpath) {
262         HtmlForm form = null;
263         HtmlElement e = getHtmlElementByXPath(xpath);
264         if ( e instanceof HtmlForm ) {
265             form = (HtmlForm) e;
266         }
267         return form;
268     }
269 
270     /***
271      * Gets an HtmlInput tag based on the current workingForm or the currently active page if the workingForm is not set.
272      * @param fieldName - The name of the input field
273      * 
274      * @return HtmlForm
275      */
276     public HtmlInput getHtmlInputByName(String fieldName) {
277         HtmlInput input = null;
278         HtmlForm form = delegate.getWorkingForm();
279         if ( form != null ) {
280             input = form.getInputByName(fieldName);
281         } else {
282             HtmlElement element = getHtmlElementByXPath("//input[@name='"+fieldName+"']");
283             if ( element != null && element instanceof HtmlInput ) {
284                 input = (HtmlInput)element;
285             }
286         }
287         return input;
288     }
289 
290     /***
291      * Gets an HtmlInput tag based on the current workingForm or the currently active page if the workingForm is not set.
292      * @param fieldName - The name of the input field
293      * @param fieldValue - The value of the input field
294      * 
295      * @return HtmlForm
296      */
297     public HtmlInput getHtmlInputByNameAndValue(String fieldName, String fieldValue) {
298         HtmlInput input = null;
299         HtmlForm form = delegate.getWorkingForm();
300         String xpath = "//input[@name='"+fieldName+"' and @value='"+fieldValue+"']";
301         if ( form != null ) {
302             input = (HtmlInput) getHtmlElementByXPath(form, xpath);
303         } else {
304             input = (HtmlInput) getHtmlElementByXPath(xpath);
305         }
306         return input;
307     }
308 
309     /***
310      * Navigates to the provided URL
311      * @param url - the url to navigate to.
312      */
313     public void navigate(String url) {
314         try {
315             WebClient client = delegate.getWebClient();
316             URL u = new URL(url);
317             client.getPage(u);
318         } catch ( MalformedURLException mfue ) {
319             throw new RuntimeException("The provided url '"+url+"' is not valid.", mfue);
320         } catch ( IOException ioe ) {
321             throw new RuntimeException("Could not coneect to '"+url+"'.", ioe);
322         }
323     }
324 
325     /***
326      * Checks or unchecks the checkbox that either exists in the workingForm or in the current page
327      * @param fieldName - The name of the input field to set the value of
328      * @param checked - set to <code>true</code> to check the checkbox and to <code>false</code> to uncheck it
329      */
330     public void setCheckBox(String fieldName, boolean checked) {
331         HtmlInput input = getHtmlInputByName(fieldName);
332         if ( input != null ) {
333             if ( input instanceof HtmlCheckBoxInput ) {
334                 ((HtmlCheckBoxInput)input).setChecked(checked);
335             } else {
336                 throw new RuntimeException("expected checkbox, but was a '"+input.getTypeAttribute()+"' field!");
337             }
338         } else {
339             throw new RuntimeException("No checkbox named '"+fieldName+"' was found!");
340         }
341     }
342 
343     /***
344      * Checks or unchecks the checkbox that either exists in the workingForm or in the current page.
345      * This method is used for pages that contain several checkboxes with the same name, but different values
346      * @param fieldName - The name of the checkbox to check
347      * @param fieldValue - The value of the input field to check
348      * @param checked - set to <code>true</code> to check the checkbox and to <code>false</code> to uncheck it
349      */
350     public void setCheckBox(String fieldName, String fieldValue, boolean checked) {
351         HtmlInput input = getHtmlInputByNameAndValue(fieldName, fieldValue);
352         if ( input != null ) {
353             if ( input instanceof HtmlCheckBoxInput ) {
354                 ((HtmlCheckBoxInput)input).setChecked(checked);
355             } else {
356                 throw new RuntimeException("expected checkbox, but was a '"+input.getTypeAttribute()+"' field!");
357             }
358         } else {
359             throw new RuntimeException("No checkbox named '"+fieldName+"' with the value '"+fieldValue+"' was found!");
360         }
361     }
362 
363     /***
364      * Sets the file field that either exists in the workingForm or in the current page
365      * @param fieldName - The name of the input field to set the value of
366      * @param value - The value to set the input field to
367      */
368     public void setFileField(String fieldName, String value) {
369         HtmlInput input = getHtmlInputByName(fieldName);
370         setHtmlInputValue(input, value, "file");
371     }
372 
373     /***
374      * Sets the hidden field that either exists in the workingForm or in the current page
375      * @param fieldName - The name of the input field to set the value of
376      * @param value - The value to set the input field to
377      */
378     public void setHiddenField(String fieldName, String value) {
379         HtmlInput input = getHtmlInputByName(fieldName);
380         setHtmlInputValue(input, value, "hidden");
381     }
382 
383     /***
384      * Sets the password field that either exists in the workingForm or in the current page
385      * @param fieldName - The name of the input field to set the value of
386      * @param value - The value to set the input field to
387      */
388     public void setPasswordField(String fieldName, String value) {
389         HtmlInput input = getHtmlInputByName(fieldName);
390         setHtmlInputValue(input, value, "password");
391     }
392 
393     /***
394      * Checks or unchecks the radio button that either exists in the workingForm or in the current page.
395      * @param fieldName - The name of the radio button to check
396      * @param fieldValue - The value of the radio button to check
397      * @param checked - set to <code>true</code> to check the radio button and to <code>false</code> to uncheck it
398      */
399     public void setRadioButton(String fieldName, String fieldValue, boolean checked) {
400         HtmlInput input = getHtmlInputByNameAndValue(fieldName, fieldValue);
401         if ( input != null ) {
402             if ( input instanceof HtmlRadioButtonInput ) {
403                 ((HtmlRadioButtonInput)input).setChecked(checked);
404             } else {
405                 throw new RuntimeException("expected radio, but was a '"+input.getTypeAttribute()+"' field!");
406             }
407         } else {
408             throw new RuntimeException("No radio button named '"+fieldName+"' with the value '"+fieldValue+"' was found!");
409         }
410     }
411 
412     /***
413      * Selects or unselects the option by its index order that either exists in the workingForm or in the current page.
414      * @param fieldName - The name of the select field to select
415      * @param index - The nth option in the list. 1st option = 1
416      * @param selected - set to <code>true</code> to select and to <code>false</code> to unselect it
417      */
418     public void setSelectFieldByIndex(String fieldName, int index, boolean selected) {
419         HtmlOption option = null;
420         HtmlForm form = delegate.getWorkingForm();
421         String xpath = "//select[@name='"+fieldName+"']/option["+index+"]";
422         HtmlElement element = null;
423         if ( form != null ) {
424             element = getHtmlElementByXPath(form, xpath);
425         } else {
426             element = getHtmlElementByXPath(xpath);
427         }
428         if ( element != null && element instanceof HtmlOption ) {
429             option = (HtmlOption)element;
430             option.setSelected(selected);
431         } else {
432             throw new RuntimeException("select field with the name '"+fieldName+"' and option was not found!");
433         }
434     }
435 
436     /***
437      * Selects or unselects the option with the displayed text that either exists in the workingForm or in the current page.
438      * @param fieldName - The name of the select field to select
439      * @param optionText - The text displayed in the drop-down
440      * @param selected - set to <code>true</code> to select and to <code>false</code> to unselect it
441      */
442     public void setSelectFieldByOptionText(String fieldName, String optionText, boolean selected) {
443         HtmlOption option = null;
444         HtmlForm form = delegate.getWorkingForm();
445         String xpath = "//select[@name='"+fieldName+"']/option[text()='"+optionText+"']";
446         HtmlElement element = null;
447         if ( form != null ) {
448             element = getHtmlElementByXPath(form, xpath);
449         } else {
450             element = getHtmlElementByXPath(xpath);
451         }
452         if ( element != null && element instanceof HtmlOption ) {
453             option = (HtmlOption)element;
454             option.setSelected(selected);
455         } else {
456             throw new RuntimeException("select field with the name '"+fieldName+"' was not found!");
457         }
458     }
459 
460     /***
461      * Selects or unselects the option with the given attribute value that either exists in the workingForm or in the current page.
462      * @param fieldName - The name of the select field to select
463      * @param valueAttribute - The value of option field to select
464      * @param selected - set to <code>true</code> to select and to <code>false</code> to unselect it
465      */
466     public void setSelectFieldByValue(String fieldName, String valueAttribute, boolean selected) {
467         HtmlSelect select = null;
468         HtmlForm form = delegate.getWorkingForm();
469         if ( form != null ) {
470             select = form.getSelectByName(fieldName);
471         } else {
472             HtmlElement element = getHtmlElementByXPath("//select[@name='"+fieldName+"']");
473             if ( element != null && element instanceof HtmlSelect ) {
474                 select = (HtmlSelect)element;
475             }
476         }
477         if ( select != null ) {
478             select.setSelectedAttribute(valueAttribute, selected);
479         } else {
480             throw new RuntimeException("select field with the name '"+fieldName+"' was not found!");
481         }
482     }
483 
484     /***
485      * Selects or unselects the option defined by XPath.
486      * @param xpath - The XPath expression that matches an HtmlOption (not a select) to be selected
487      * @param selected - set to <code>true</code> to select and to <code>false</code> to unselect it
488      */
489     public void setSelectFieldByXPath(String xpath, boolean selected) {
490         HtmlOption option = null;
491         HtmlElement element = null;
492         element = getHtmlElementByXPath(xpath);
493         if ( element != null && element instanceof HtmlOption ) {
494             option = (HtmlOption)element;
495             option.setSelected(selected);
496         } else {
497             throw new RuntimeException("select field defined by '"+xpath+"' was not found!");
498         }
499     }
500 
501     /***
502      * Sets the text area that either exists in the workingForm or in the current page
503      * @param fieldName - The name of the input field to set the value of
504      * @param value - The value to set the input field to
505      */
506     public void setTextArea(String fieldName, String value) {
507         HtmlTextArea textArea = null;
508         HtmlForm form = delegate.getWorkingForm();
509         if ( form != null ) {
510             List textAreas = form.getTextAreasByName(fieldName);
511             if ( textAreas.size() > 0 ) {
512                 textArea = (HtmlTextArea)textAreas.get(0);
513             }
514         } else {
515             HtmlElement element = getHtmlElementByXPath("//textarea[@name='"+fieldName+"']");
516             if ( element != null && element instanceof HtmlTextArea ) {
517                 textArea = (HtmlTextArea)element;
518             }
519         }
520         if ( textArea != null ) {
521             textArea.setText(value);
522         } else {
523             throw new RuntimeException("textarea with the name '"+fieldName+"' was not found!");
524         }
525     }
526 
527     /***
528      * Sets the text field that either exists in the workingForm or in the current page
529      * @param fieldName - The name of the input field to set the value of
530      * @param value - The value to set the input field to
531      */
532     public void setTextField(String fieldName, String value) {
533         HtmlInput input = getHtmlInputByName(fieldName);
534         setHtmlInputValue(input, value, "text");
535     }
536 
537     /***
538      * Sets an input field type's value
539      * @param input - The HtmlInput to set the value on
540      * @param value - The value to set the input element to.
541      * @param type - The input type. If this does not match the given field, then this method will fail
542      */
543     public void setHtmlInputValue(HtmlInput input, String value, String type) {
544         if ( input != null ) {
545             String elementType = input.getAttributeValue("type");
546             if ( type.equalsIgnoreCase(elementType) ) {
547                 input.setValueAttribute(value);
548             } else {
549                 throw new RuntimeException("input field defined '"+input.getTagName()+"' of type '"+elementType+"' was not expected '"+type+"'!");
550             }
551         } else {
552             throw new RuntimeException("input field was null!");
553         }
554     }
555 
556     /***
557      * Sets an input field type's value
558      * @param xpath - An XPath expression that matches the desired HtmlInput Element.
559      * @param value - The value to set the input element to.
560      * @param type - The input type. If this does not match the given field, then this method will fail
561      */
562     public void setHtmlInputValueByXPath(String xpath, String value, String type) {
563         HtmlElement element = getHtmlElementByXPath(xpath);
564         if ( element != null && element instanceof HtmlInput ) {
565             HtmlInput input = (HtmlInput) element;
566             setHtmlInputValue(input, value, type);
567         } else {
568             throw new RuntimeException("Could not find the input element defined by '"+xpath+"'");
569         }
570     }
571 
572     /***
573      * Store the contents of the currently active window
574      * @param fName - The name of the file to store the source to.
575      */
576     public File store(String fName) throws IOException{
577         WebResponse res = getCurrentWebResponse();
578         File stateFile = null;
579         if ( res != null ) {
580             String html = res.getContentAsString();
581             String charset = res.getContentCharSet();
582             String filename = fName+".html";
583             if ( html != null && html.length() > 0 ) {
584                 stateFile = new File(filename);
585                 if ( charset != null && charset.length() > 0 ) {
586                     JameleonUtility.recordResultsToFile(stateFile, html, charset);
587                 } else {
588                     JameleonUtility.recordResultsToFile(stateFile, html);
589                 }
590             }
591         }
592         return stateFile;
593     }
594 
595     /***
596      * Checks that the the provided XPath expression matches something on the current page.
597      * @param xpath - The XPath expression to evaulate
598      * 
599      * @return boolean - true if the XPath expresssion matches
600      */
601     public boolean xPathMatches(String xpath) {
602         boolean matches = (getHtmlElementByXPath(xpath) != null);
603         return matches;
604     }
605 }