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
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
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();
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
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 }