1 16 package com.blandware.atleap.webapp.webtest; 17 18 import com.blandware.atleap.common.util.ConvertUtil; 19 import com.blandware.atleap.common.util.StringUtil; 20 import com.gargoylesoftware.htmlunit.ElementNotFoundException; 21 import com.gargoylesoftware.htmlunit.Page; 22 import com.gargoylesoftware.htmlunit.WebClient; 23 import com.gargoylesoftware.htmlunit.html.*; 24 import com.gargoylesoftware.htmlunit.html.xpath.HtmlUnitXPath; 25 import junit.framework.TestCase; 26 import org.apache.commons.logging.Log; 27 import org.apache.commons.logging.LogFactory; 28 import org.apache.oro.text.regex.MalformedPatternException; 29 import org.apache.oro.text.regex.Pattern; 30 import org.apache.oro.text.regex.PatternCompiler; 31 import org.apache.oro.text.regex.PatternMatcher; 32 import org.apache.oro.text.regex.Perl5Compiler; 33 import org.apache.oro.text.regex.Perl5Matcher; 34 import org.jaxen.JaxenException; 35 import org.jaxen.XPath; 36 37 import java.io.File ; 38 import java.io.FileOutputStream ; 39 import java.io.IOException ; 40 import java.io.InputStream ; 41 import java.net.URL ; 42 import java.text.MessageFormat ; 43 import java.util.Calendar ; 44 import java.util.Date ; 45 import java.util.HashMap ; 46 import java.util.Iterator ; 47 import java.util.List ; 48 import java.util.Map ; 49 import java.util.Properties ; 50 import java.util.ResourceBundle ; 51 52 59 public abstract class BaseWebTestCase extends TestCase { 60 61 64 protected static final String INPUT_TYPE_TEXT = "text"; 65 66 69 protected static final String INPUT_TYPE_PASSWORD = "password"; 70 71 74 protected static final String INPUT_TYPE_SUBMIT = "submit"; 75 76 77 80 protected static final String INPUT_TYPE_BUTTON = "button"; 81 82 85 protected static final String INPUT_TYPE_RESET = "reset"; 86 87 90 protected static final String INPUT_TYPE_CHECKBOX = "checkbox"; 91 92 95 protected static final String INPUT_TYPE_RADIO = "radio"; 96 97 100 protected static final String INPUT_TYPE_FILE = "file"; 101 102 105 protected static final String INPUT_TYPE_HIDDEN = "hidden"; 106 107 110 protected static final String INPUT_TYPE_IMAGE = "image"; 111 112 115 protected static final MessageFormat UNEXPECTED_INPUT_TYPE = new MessageFormat ("Input with name '{0}' in form '{1}' does not seem to be of type '{2}', though expected"); 116 117 120 protected static final String ADMIN_USER_NAME = "admin"; 121 122 125 protected static final String ADMIN_PASSWORD = "password"; 126 127 130 protected transient final Log log = LogFactory.getLog(getClass()); 131 132 135 protected String responseFilePrefix = "response-"; 136 137 140 protected String baseUrl = null; 141 142 145 protected Boolean saveResponse = null; 146 147 151 protected static Properties messageBundle = new Properties (); 152 153 156 protected WebClient webClient; 157 158 161 protected HtmlPage lastResponse; 162 163 166 protected static Map config = new HashMap (); 167 168 static { 170 String bundleName = BaseWebTestCase.class.getPackage().getName(); 172 bundleName += ".WebTest"; 173 174 ResourceBundle rb = ResourceBundle.getBundle(bundleName); 175 config = ConvertUtil.convertBundleToMap(rb); 176 177 178 String messageBundleFileName = (String ) config.get("messageBundle"); 179 if ( messageBundleFileName != null && messageBundleFileName.length() > 0 ) { 180 InputStream in = null; 181 try { 182 in = BaseWebTestCase.class.getClassLoader().getResourceAsStream("ApplicationResources_en.properties"); 183 if ( in != null ) { 184 messageBundle.load(in); 185 } else { 186 throw new ExceptionInInitializerError ("Message bundle file (" + messageBundleFileName + ") could not be found in classpath"); 187 } 188 } catch ( IOException e ) { 189 throw new ExceptionInInitializerError (e); 190 } finally { 191 try { 192 if ( in != null ) { 193 in.close(); 194 } 195 } catch ( Exception e ) { 196 } 197 } 198 } 199 } 200 201 204 public BaseWebTestCase() { 205 super(); 206 init(); 207 } 208 209 214 public BaseWebTestCase(String name) { 215 super(name); 216 init(); 217 } 218 219 222 protected final void init() { 223 webClient = new WebClient(); 224 webClient.setJavaScriptEnabled(false); 225 webClient.addRequestHeader("Accept-Language", "en"); 226 } 227 228 229 235 protected String getMessage(String key) { 236 return messageBundle.getProperty(key); 237 } 238 239 244 protected String createBaseUrl() { 245 246 if ( baseUrl != null ) { 247 return baseUrl; 248 } 249 250 String protocol = (String ) config.get("protocol"); 252 if ( protocol == null || !"http".equalsIgnoreCase(protocol) && !"https".equalsIgnoreCase(protocol) ) { 253 if ( log.isWarnEnabled() ) { 255 log.warn("Protocol does not specified or is unsupported. Using http instead"); 256 } 257 protocol = "http"; 258 } else { 259 protocol = protocol.toLowerCase(); 260 } 261 262 String host = (String ) config.get("host"); 263 264 if ( host == null || host.length() == 0 ) { 265 if ( log.isWarnEnabled() ) { 266 log.warn("Host does not specified, assume localhost"); 267 } 268 host = "localhost"; 269 } 270 271 String portString = (String ) config.get("port"); 272 int port; 273 try { 274 port = Integer.parseInt(portString); 275 } catch ( RuntimeException e ) { 276 if ( log.isWarnEnabled() ) { 277 log.warn(e.getClass().getName() + ": No port specified, or value is incorrect. Using default, according to protocol"); 278 } 279 if ( "http".equals(protocol) ) { 280 port = 80; 281 } else { 282 port = 443; 284 } 285 } 286 287 String contextPath = (String ) config.get("contextPath"); 288 if ( contextPath == null ) { 289 contextPath = ""; 290 } 291 292 if ( !contextPath.startsWith("/") ) { 293 contextPath = "/" + contextPath; 294 } 295 296 baseUrl = protocol + "://" + host + ":" + port + contextPath; 297 return baseUrl; 298 } 299 300 305 protected boolean isSaveResponse() { 306 if ( saveResponse != null ) { 307 return saveResponse.booleanValue(); 308 } 309 310 saveResponse = Boolean.TRUE; 311 Object saveReponseParam = config.get("saveResponse"); 312 if ( saveReponseParam != null ) { 313 if ( saveReponseParam instanceof Boolean ) { 314 saveResponse = (Boolean ) saveReponseParam; 315 } else if ( saveReponseParam instanceof String ) { 316 saveResponse = Boolean.valueOf((String ) saveReponseParam); 317 } 318 } 319 return saveResponse.booleanValue(); 320 } 321 322 327 protected String getResultPath() { 328 return (String ) config.get("resultPath"); 329 } 330 331 338 protected File createResponseFile() { 339 Calendar cal = Calendar.getInstance(); 340 cal.setTime(new Date ()); 341 342 StringBuffer fileName = new StringBuffer (); 344 String resultPath = getResultPath(); 345 if ( resultPath != null && resultPath.length() > 0 ) { 346 if ( !resultPath.endsWith(File.separator) ) { 347 resultPath += File.separator; 348 } 349 fileName.append(resultPath); 350 } 351 352 fileName.append(responseFilePrefix); 354 355 fileName.append(cal.get(Calendar.YEAR)).append("-"); 357 fileName.append(StringUtil.completeNumber(cal.get(Calendar.MONTH) + 1, 2)).append("-"); 358 fileName.append(StringUtil.completeNumber(cal.get(Calendar.DATE), 2)); 359 360 fileName.append("_"); 362 363 fileName.append(StringUtil.completeNumber(cal.get(Calendar.HOUR_OF_DAY), 2)).append("-"); 365 fileName.append(StringUtil.completeNumber(cal.get(Calendar.MINUTE), 2)).append("-"); 366 fileName.append(StringUtil.completeNumber(cal.get(Calendar.SECOND), 2)); 367 368 fileName.append("_"); 370 371 fileName.append(StringUtil.completeNumber(cal.get(Calendar.MILLISECOND), 3)); 373 374 fileName.append(".html"); 376 return new File (fileName.toString()); 377 } 378 379 386 protected void saveResponse(File responseFile, Page response) throws IOException { 387 FileOutputStream out = new FileOutputStream (responseFile); 388 out.write(response.getWebResponse().getResponseBody()); 389 out.close(); 390 } 391 392 398 protected void invoke(String relativeUrl) throws IOException { 399 String absoluteUrl = createBaseUrl(); 400 if ( !absoluteUrl.endsWith("/") ) { 401 absoluteUrl += "/"; 402 } 403 404 if ( relativeUrl.startsWith("/") ) { 405 relativeUrl = relativeUrl.substring(1); 406 } 407 408 absoluteUrl += relativeUrl; 409 410 setLastResponse((HtmlPage) webClient.getPage(new URL (absoluteUrl))); 411 } 412 413 422 protected void login(String userName, String password) throws IOException { 423 424 invoke("/admin.do"); 426 427 HtmlForm loginForm = getFormByName("loginForm"); 429 430 HtmlTextInput userNameField = (HtmlTextInput) getInputByName(loginForm, "j_username", INPUT_TYPE_TEXT); 432 HtmlPasswordInput passwordField = (HtmlPasswordInput) getInputByName(loginForm, "j_password", INPUT_TYPE_PASSWORD); 433 434 userNameField.setValueAttribute(userName); 435 passwordField.setValueAttribute(password); 436 437 clickButtonWithLabel(loginForm, getMessage("core.commons.buttons.login")); 439 440 getFormByName("logoutForm"); 442 443 if ( log.isInfoEnabled() ) { 445 log.info("User '" + userName + "' has successfully logged in"); 446 } 447 } 448 449 455 protected void login() throws IOException { 456 login(ADMIN_USER_NAME, ADMIN_PASSWORD); 457 } 458 459 464 protected void logout() throws IOException { 465 if ( lastResponse == null ) { 466 fail("No request has been done yet, no response received. Could not log out from application"); 467 } 468 469 HtmlForm logoutForm = getFormByName("logoutForm"); 470 471 clickButtonWithLabel(logoutForm, getMessage("core.commons.buttons.logout")); 472 473 getFormByName("loginForm"); 475 476 if ( log.isInfoEnabled() ) { 478 log.info("Logout has been successfully performed"); 479 } 480 } 481 482 488 protected void setLastResponse(HtmlPage response) throws IOException { 489 lastResponse = response; 490 if ( isSaveResponse() ) { 491 saveResponse(createResponseFile(), lastResponse); 492 } 493 } 494 495 507 protected HtmlForm getFormByName(String name) { 508 HtmlForm form = null; 509 try { 510 form = lastResponse.getFormByName(name); 511 } catch ( ElementNotFoundException e ) { 512 fail("No form with name '" + name + "' could be found in the last response"); 513 } 514 return form; 515 } 516 517 525 protected HtmlInput getInputByName(HtmlForm form, String name, String expectedType) { 526 HtmlInput input = null; 527 try { 528 input = form.getInputByName(name); 529 boolean error = false; 530 if ( expectedType != null ) { 531 error = expectedType.equalsIgnoreCase(INPUT_TYPE_BUTTON) && !(input instanceof HtmlButtonInput) || 532 expectedType.equalsIgnoreCase(INPUT_TYPE_CHECKBOX) && !(input instanceof HtmlCheckBoxInput) || 533 expectedType.equalsIgnoreCase(INPUT_TYPE_FILE) && !(input instanceof HtmlFileInput) || 534 expectedType.equalsIgnoreCase(INPUT_TYPE_HIDDEN) && !(input instanceof HtmlHiddenInput) || 535 expectedType.equalsIgnoreCase(INPUT_TYPE_IMAGE) && !(input instanceof HtmlImageInput) || 536 expectedType.equalsIgnoreCase(INPUT_TYPE_PASSWORD) && !(input instanceof HtmlPasswordInput) || 537 expectedType.equalsIgnoreCase(INPUT_TYPE_RADIO) && !(input instanceof HtmlRadioButtonInput) || 538 expectedType.equalsIgnoreCase(INPUT_TYPE_RESET) && !(input instanceof HtmlResetInput) || 539 expectedType.equalsIgnoreCase(INPUT_TYPE_SUBMIT) && !(input instanceof HtmlSubmitInput) || 540 expectedType.equalsIgnoreCase(INPUT_TYPE_TEXT) && !(input instanceof HtmlTextInput); 541 } 542 if ( error ) { 543 fail(UNEXPECTED_INPUT_TYPE.format(new Object []{name, form.getNameAttribute(), expectedType})); 544 } 545 } catch ( ElementNotFoundException e ) { 546 fail("No input field with name '" + name + "' is available in form with name '" + form.getNameAttribute() + "'"); 547 } 548 return input; 549 } 550 551 562 protected HtmlSelect getSelectByName(HtmlForm form, String name) { 563 HtmlSelect select = null; 564 try { 565 select = form.getSelectByName(name); 566 } catch ( ElementNotFoundException e ) { 567 fail("Select with name '" + name + "' could not be found in form '" + form.getNameAttribute() + "'"); 568 } 569 return select; 570 } 571 572 581 protected void clickButtonWithLabel(HtmlForm form, String label) throws IOException { 582 HtmlSubmitInput button = null; 583 HtmlInput tmp = null; 584 if ( form != null ) { 585 try { 586 tmp = form.getInputByValue(label); 587 } catch ( ElementNotFoundException e ) { 588 fail("No button with label=" + label + " could be found in form '" + form.getNameAttribute() + "'"); 589 } 590 } else { 591 List forms = lastResponse.getForms(); 592 if ( forms == null || forms.isEmpty() ) { 593 fail("No form could be found on the page, returned in last response"); 594 } 595 Iterator formsIterator = forms.iterator(); 596 while ( formsIterator.hasNext() ) { 597 form = (HtmlForm) formsIterator.next(); 598 try { 599 tmp = form.getInputByValue(label); 600 } catch ( ElementNotFoundException e ) { 601 continue; 603 } 604 } 605 if ( tmp == null ) { 606 fail("No button with label '" + label + "' could be found on the page, returned in last response"); 608 } 609 } 610 if ( !(tmp instanceof HtmlSubmitInput) ) { 611 fail("Input with value=" + label + " should be submit button but it does not"); 612 } else { 613 button = (HtmlSubmitInput) tmp; 614 } 615 616 setLastResponse((HtmlPage) button.click()); 617 } 618 619 620 628 protected Object evaluateXPath(String xpathExpr, HtmlPage page) throws JaxenException { 629 return evaluateXPath(xpathExpr, page, true); 630 } 631 632 640 protected Object evaluateXPath(String xpathExpr, HtmlPage page, boolean singleNode) throws JaxenException { 641 XPath xpath = new HtmlUnitXPath(xpathExpr); 642 if ( singleNode ) { 643 return xpath.selectSingleNode(page); 644 } else { 645 return xpath.selectNodes(page); 646 } 647 } 648 649 651 658 protected void assertXPathResultNotNull(String xpathExpr) throws JaxenException { 659 Object result = evaluateXPath(xpathExpr, lastResponse); 660 assertNotNull(result); 661 } 662 663 671 protected void assertXPathResultEquals(String xpathExpr, String expectedValue) throws JaxenException { 672 String actual = (String ) evaluateXPath(xpathExpr, lastResponse); 673 assertEquals(expectedValue, actual); 674 } 675 676 684 protected void assertXPathResultMatches(String xpathExpr, String regex) throws JaxenException, MalformedPatternException { 685 String actual = (String ) evaluateXPath(xpathExpr, lastResponse); 686 assertMatches(actual, regex); 687 } 688 689 695 protected void assertGreaterThan(Comparable actual, Comparable compareTo) { 696 assertTrue(actual.compareTo(compareTo) > 0); 697 } 698 699 706 protected void assertMatches(String input, String regex) throws MalformedPatternException { 707 PatternCompiler compiler = new Perl5Compiler(); 708 PatternMatcher matcher = new Perl5Matcher(); 709 Pattern pattern = compiler.compile(regex); 710 assertTrue(matcher.matches(input, pattern)); 711 } 712 713 715 public void testLoginAndLogout() throws IOException { 716 login(); 717 logout(); 718 } 719 720 } 721 | Popular Tags |