View Javadoc

1   package com.flexiblewebsolutions.xdriveunit;
2   
3   import java.io.BufferedOutputStream;
4   import java.io.File;
5   import java.io.FileNotFoundException;
6   import java.io.FileOutputStream;
7   import java.io.IOException;
8   import java.io.PrintStream;
9   
10  import net.sourceforge.jwebunit.WebTester;
11  
12  import org.apache.log4j.Logger;
13  
14  import com.flexiblewebsolutions.io.util.FileUtils;
15  import com.gargoylesoftware.htmlunit.ElementNotFoundException;
16  import com.gargoylesoftware.htmlunit.Page;
17  import com.gargoylesoftware.htmlunit.WebClient;
18  import com.gargoylesoftware.htmlunit.html.ClickableElement;
19  import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
20  import com.gargoylesoftware.htmlunit.html.HtmlForm;
21  import com.gargoylesoftware.htmlunit.html.HtmlPage;
22  import com.meterware.httpunit.HttpUnitOptions;
23  
24  /***
25   * XML Driven Test Case that is used to test web applications.
26   * 
27   * @author Donavon Buss
28   */
29  public abstract class WebXDriveTestCase extends XDriveTestCase
30  {
31      static Logger logger = Logger.getLogger( WebXDriveTestCase.class.getName() );
32  
33      /*** HTML Unit HTTP Testing API */
34      public final static int HTMLUNIT = 1;
35  
36      /*** JWEBUnit Http Testing API */
37      public final static int JWEBUNIT = 5;
38  
39      /*** Some parameters specific to web test case */
40      private boolean _ShowJSErrors = false;
41  
42      private boolean _CachePages = false;
43  
44      /*** Framework Test API clients */
45      protected WebTester _WebTester = null;
46  
47      protected WebClient _WebClient = null;
48      
49      /*** HtmlUnit classes used to keep track of position */
50      private HtmlPage _CurrentPage = null;
51      private HtmlForm _CurrentForm = null;
52  
53      /*** Cache index for all visited pages */
54      private StringBuffer _CacheIndexXML = null;
55  
56      /***
57       * @see junit.framework.TestCase#setUp()
58       */
59      protected void setUp() throws Exception
60      {
61          super.setUp();
62          if( _CachePages )
63          {
64              _CacheIndexXML = new StringBuffer();
65              _CacheIndexXML.append( "<pagecache>\n" );
66              _CacheIndexXML.append( "<testmethod>" );
67              _CacheIndexXML.append( _ThreadName );
68              _CacheIndexXML.append( "_" );
69              _CacheIndexXML.append( this.getName() );
70              _CacheIndexXML.append( "</testmethod>\n" );
71              _CacheIndexXML.append( "<pages>\n" );
72          }
73      }
74  
75      /***
76       * Sets the currently displayed page into a variable
77       * @param pPage - The currently displayed page
78       */
79      private void setCurrentPage( Page pPage )
80      {
81          if( pPage instanceof HtmlPage )
82          {
83              _CurrentPage = (HtmlPage) pPage;
84          }
85      }
86      
87      /*** 
88       * Retrieves the currently selected page
89       * @return Current page 
90       */
91      private Page getCurrentPage()
92      {
93          if( _CurrentPage != null )
94          {
95              return( _CurrentPage );
96          }
97          else
98          {
99              throw new RuntimeException( "No current page." );
100         }
101     }
102 
103     /***
104      * @see junit.framework.TestCase#tearDown()
105      */
106     protected void tearDown() throws Exception
107     {
108         super.tearDown();
109 
110         // print out the javascript errors to the console
111         if( _ShowJSErrors )
112         {
113             if( getTestFramework() == JWEBUNIT )
114             {
115                 String[] messages = HttpUnitOptions.getScriptErrorMessages();
116                 for( int i = 0; i < messages.length; i++ )
117                 {
118                     System.err.println( "MESSAGE: " + messages[i] );
119                 }
120             }
121             else
122             {
123                 System.err.println( "JS Errors Dump not supported." );
124             }
125         }
126 
127         // write index file for pages cached
128         if( _CachePages )
129         {
130             _CacheIndexXML.append( "</pages>\n" );
131             _CacheIndexXML.append( "</pagecache>\n" );
132             writeCacheIndexXML();
133         }
134     }
135 
136     /***
137      * Creates a master page where all cached pages are linked to and accessible
138      * 
139      * TODO: Create Stylesheet to translate the XML
140      */
141     private void writeCacheIndexXML()
142     {
143 
144         String filename = _ThreadName + "_" + getName();
145         File outFile = new File( getTestTmpDirectory(), filename + ".xml" );
146         logger.debug( "writeCacheIndexXML to " + outFile.getAbsolutePath() );
147         new FileUtils().writeFile( outFile, _CacheIndexXML );
148         _CacheIndexXML = null;
149         outFile = null;
150         filename = null;
151     }
152 
153     /***
154      * Constructor for WebXMLTestCase
155      * 
156      * @param pName -
157      *            Name of test method
158      * @param pTestInput -
159      *            XML containing test input
160      * @param pTestOptions -
161      *            XML containing test options
162      * @param pThreadName -
163      *            Name of thread running test, can be blank
164      */
165     public WebXDriveTestCase( String pName, StringBuffer pTestInput,
166             StringBuffer pTestOptions, String pThreadName )
167     {
168         super( pName, pTestInput, pTestOptions, pThreadName );
169 
170         initWebTestOptions();
171         initTestFramework(); // default
172     }
173 
174     /***
175      * Initializes preferred test framework for executing the test.     *
176      */
177     private void initTestFramework()
178     {
179         int framework = getTestFramework();
180         if( framework == HTMLUNIT )
181         {
182             _WebTester = null;
183             _WebClient = new WebClient();
184         }
185         else if( framework == JWEBUNIT )
186         {
187             _WebClient = null;
188             _WebTester = new WebTester();
189         }
190     }
191 
192     public abstract int getTestFramework();
193 
194     /***
195      * Initialize settings used specifically for web tests.
196      *  
197      */
198     private void initWebTestOptions()
199     {
200         logger.debug( "initWebTestOptions" );
201         _CachePages = checkBooleanParameter( "cache" );
202         _ShowJSErrors = checkBooleanParameter( "showJSErrors" );
203     }
204 
205     /***
206      * Clicks on a link 
207      * 
208      * @param pLinkName - Name attribute of the link
209      * @param pPageDescription - Description of action for logging
210      */
211     protected void clickLink( String pLinkName, String pPageDescription )
212     {
213         logger.debug( "clickLink: " + pLinkName );
214         if( getTestFramework() == JWEBUNIT )
215         {
216             _WebTester.clickLink( pLinkName );
217         }
218         else if( getTestFramework() == HTMLUNIT )
219         {
220             try
221             {
222                 HtmlAnchor anchor = ( (HtmlPage) getCurrentPage() ).getAnchorByName( pLinkName );
223                 setCurrentPage( anchor.click() );
224             }
225             catch( ElementNotFoundException e )
226             {
227                 fail( "Could not find link: " + pLinkName );
228             }
229             catch( IOException e )
230             {
231                 fail( "Error clicking link: " + pLinkName );
232             }
233         }
234         cachePage( pLinkName, pPageDescription );
235     }
236 
237     /***
238      * Clicks a variety of html element types that are clickable. 
239      * 
240      * @param pId - Id attribute of the element
241      * @param pDescription - Description of action for logging
242      */
243     protected void clickElementById( String pId, String pDescription )
244     {
245         if( getTestFramework() == HTMLUNIT )
246         {
247             try
248             {
249                 ClickableElement clickable = (ClickableElement) ( (HtmlPage) getCurrentPage() ).getHtmlElementById( pId );
250                 setCurrentPage( clickable.click() );
251             }
252             catch( ElementNotFoundException e )
253             {
254                 fail( "Could not find element: " + pId );
255             }
256             catch( IOException e )
257             {
258                 fail( "Error clicking element: " + pId );
259             }
260         }
261         else
262         {
263             throw new RuntimeException(
264                     "Method not implemented for this test framework." );
265         }
266         cachePage( pId, pDescription );
267     }
268     
269     /***
270      * Clicks an html button 
271      * 
272      * @param pButtonID - Id attribute of the button
273      * @param pButtonDesc - Description of action for logging
274      */
275     protected void clickButton( String pButtonID, String pButtonDesc )
276     {
277         logger.debug( "clickButton: " + pButtonID );
278         if( getTestFramework() == JWEBUNIT )
279         {
280             _WebTester.clickButton( pButtonID );
281             cachePage( pButtonID, pButtonDesc );
282         }
283         else if( getTestFramework() == HTMLUNIT )
284         {
285             clickElementById( pButtonID, pButtonDesc );
286         }
287     }
288 
289     /***
290      * Clicks the link of an image 
291      * 
292      * @param pLinkImg - Src attribute of image
293      * @param pLinkDesc - Description of action for logging
294      */
295     protected void clickLinkWithImage( String pLinkImg, String pLinkDesc )
296     {
297         logger.debug( "clickLinkWithImage: " + pLinkImg );
298         if( getTestFramework() == JWEBUNIT )
299         {
300             _WebTester.clickLinkWithImage( pLinkImg );
301         }
302         else if( getTestFramework() == HTMLUNIT )
303         {
304             throw new RuntimeException(
305                     "The clickLinkWithImage method not supported for HTML Unit" );
306         }
307         cachePage( pLinkImg, pLinkDesc );
308     }
309 
310     /***
311      * Clicks the link on a page using text between anchor tag
312      * 
313      * @param pLinkText - Link text as it appears to user.
314      * @param pLinkDesc - Description of action for logging
315      */
316     protected void clickLinkWithText( String pLinkText, String pLinkDesc )
317     {
318         if( getTestFramework() == JWEBUNIT )
319         {
320             clickLinkWithText( pLinkText, 1, pLinkDesc );
321         }
322         else if( getTestFramework() == HTMLUNIT )
323         {
324             logger.debug( "clickLinkWithText: " + pLinkText );
325             try
326             {
327                 setCurrentPage( ( (HtmlPage) getCurrentPage() ).getFirstAnchorByText( pLinkText ).click() );
328             }
329             catch( ElementNotFoundException e )
330             {
331                 fail( "Could not find link with text: " + pLinkText );
332             }
333             catch( IOException e )
334             {
335                 fail( "Error clicking link with text: " + pLinkText );
336             }
337             cachePage( pLinkText, pLinkDesc );
338         }
339     
340     }
341 
342     /***
343      * Clicks the link on a page using text between anchor tag
344      * 
345      * @param pLinkText - Link text as it appears to user.
346      * @param pIndex - Numbered occurence in page if more than one Link
347      * @param pLinkDesc - Description of action for logging
348      */
349     protected void clickLinkWithText( String pLinkText, int pIndex,
350             String pLinkDesc )
351     {
352         logger.debug( "clickLinkWithText: " + pLinkText );
353         if( getTestFramework() == JWEBUNIT )
354         {
355             _WebTester.clickLinkWithText( pLinkText, pIndex );
356         }
357         else if( getTestFramework() == HTMLUNIT )
358         {
359             throw new RuntimeException(
360                     "The clickLinkWithText(String,int,String) method not supported for HTML Unit" );
361         }
362         cachePage( pLinkText, pLinkDesc );
363     }
364 
365     /***
366      * Submits the form that has been previously set.
367      * 
368      * @param pSubmitDesc - Description of action for logging
369      */
370     protected void submit( String pSubmitDesc )
371     {
372         logger.debug( "submit: " + pSubmitDesc );
373         if( getTestFramework() == JWEBUNIT )
374         {
375             _WebTester.submit();
376         }
377         else if( getTestFramework() == HTMLUNIT )
378         {
379             try
380             {
381                 setCurrentPage( _CurrentForm.submit() );
382                 _CurrentForm = null;
383             }
384             catch( IOException e )
385             {
386                 fail( "Error submitting the form." );
387             }
388         }
389         cachePage( "SUBMIT", pSubmitDesc );
390     }
391 
392     /***
393      * Submits the form specified by the supplied name
394      * @param pFormName - Name of form to submit
395      * @param pSubmitDesc - Description of action for logging
396      */
397     protected void submit( String pFormName, String pSubmitDesc )
398     {
399         logger.debug( "submit: " + pSubmitDesc );
400         if( getTestFramework() == JWEBUNIT )
401         {
402             _WebTester.submit( pFormName );
403             cachePage( "SUBMIT - " + pFormName, pSubmitDesc );
404         }
405         else if( getTestFramework() == HTMLUNIT )
406         {
407             setCurrentForm( pFormName );
408             submit( pSubmitDesc );
409         }
410     }
411     
412     /***
413      * Verify that some text is included on the page
414      * @param pText - Any text that is visible on the page.
415      */
416     protected void assertTextPresent( String pText )
417     {
418         StringBuffer pageContents = dumpResponseToString();
419         assertTrue( "Text " + pText + " not found in page", pageContents.indexOf( pText ) != -1 );
420     }
421 
422     /***
423      * Sets the form to use when assigning values to fields
424      * @param pFormName - value from html <form name=""
425      */
426     protected void setCurrentForm( String pFormName )
427     {
428         _CurrentForm = ( (HtmlPage) getCurrentPage() ).getFormByName( pFormName );
429     }
430     
431     /***
432      * Save a copy of the webpage locally for later viewing
433      * 
434      * @param pPageName -
435      *            Name of link used to get document
436      * @param pPageDesc -
437      *            Short description of page contents
438      */
439     private void cachePage( String pPageName, String pPageDesc )
440     {
441         File cacheFile = null;
442         try
443         {
444             if( _CachePages )
445             {
446                 File cacheDir = new File( getTestTmpDirectory(), "cache" );
447                 if( !cacheDir.exists() )
448                 {
449                     cacheDir.mkdir();
450                 }
451 
452                 cacheFile = File.createTempFile( _ThreadName + "cache",
453                         ".html", cacheDir );
454 
455                 addEntryToXML( "cache/" + cacheFile.getName(), pPageName,
456                         pPageDesc );
457 
458                 dumpResponseToFile( cacheFile );
459                 cacheFile = null;
460                 cacheDir = null;
461             }
462         }
463         catch( IOException e1 )
464         {
465             logger.error( "Error creating cache file.", e1 );
466             e1.printStackTrace();
467         }
468     }
469 
470     /***
471      * Adds page information to cache xml file
472      * 
473      * @param pURL -
474      *            Temporary location of cache file
475      * @param pPageName -
476      *            Name of link or button pressed
477      * @param pPageDesc -
478      *            Short description of page or link action
479      */
480     private void addEntryToXML( String pURL, String pPageName, String pPageDesc )
481     {
482         _CacheIndexXML.append( "<page>\n" );
483         _CacheIndexXML.append( "<url>" );
484         _CacheIndexXML.append( pURL );
485         _CacheIndexXML.append( "</url>\n" );
486         _CacheIndexXML.append( "<name>" );
487         _CacheIndexXML.append( pPageName );
488         _CacheIndexXML.append( "</name>\n" );
489         if( pPageDesc != null )
490         {
491             _CacheIndexXML.append( "<description>" );
492             _CacheIndexXML.append( pPageDesc );
493             _CacheIndexXML.append( "</description>\n" );
494         }
495     }
496 
497     /***
498      * Captures current page text to specified file
499      * 
500      * @param pFile -
501      *            File to write response to
502      */
503     protected void dumpResponseToFile( File pFile )
504     {
505         PrintStream writer = null;
506         try
507         {
508             writer = new PrintStream( new BufferedOutputStream(
509                     new FileOutputStream( pFile ) ) );
510             if( getTestFramework() == JWEBUNIT )
511             {
512                 _WebTester.dumpResponse( writer );
513             }
514             else if( getTestFramework() == HTMLUNIT )
515             {
516                 Page thisPage = getCurrentPage();
517                 if( thisPage instanceof HtmlPage )
518                 {
519                     writer.println( ( (HtmlPage) getCurrentPage() ).asXml() );
520                 }
521                 else
522                 {
523                     writer.println( "Unknown page content" );
524                 }
525             }
526         }
527         catch( FileNotFoundException e )
528         {
529             logger.error( "Could not find file " + pFile.getAbsolutePath(), e );
530         }
531         finally
532         {
533             try
534             {
535                 writer.close();
536                 writer = null;
537             }
538             catch( Exception e )
539             {
540                 // ignore this error
541             }
542         }
543     }
544 
545     /***
546      * Capture current page text to a StringBuffer
547      * 
548      * @return Page Contents
549      */
550     protected StringBuffer dumpResponseToString()
551     {
552         StringBuffer returnPage = null;
553         File xFile = null;
554         try
555         {
556             xFile = File.createTempFile( "dmp", ".out", getTestTmpDirectory() );
557             dumpResponseToFile( xFile );
558             FileUtils fu = new FileUtils();
559             returnPage = fu.loadXMLFileToString( xFile );
560             fu = null;
561             xFile.delete();
562             xFile = null;
563         }
564         catch( IOException e )
565         {
566             logger.error( "Error creating or reading tmp file: "
567                     + xFile.getAbsolutePath(), e );
568         }
569         return ( returnPage );
570     }
571 }