001// Copyright 2009-2014 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.test; 016 017import com.thoughtworks.selenium.CommandProcessor; 018import com.thoughtworks.selenium.DefaultSelenium; 019import com.thoughtworks.selenium.HttpCommandProcessor; 020import com.thoughtworks.selenium.Selenium; 021import org.openqa.selenium.server.SeleniumServer; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024import org.testng.Assert; 025import org.testng.ITestContext; 026import org.testng.annotations.*; 027import org.testng.xml.XmlTest; 028 029import java.io.File; 030import java.lang.reflect.Method; 031 032/** 033 * Base class for creating Selenium-based integration test cases. This class implements all the 034 * methods of {@link Selenium} and delegates to an instance (setup once per test by 035 * {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)}. 036 * 037 * @since 5.2.0 038 */ 039public abstract class SeleniumTestCase extends Assert implements Selenium 040{ 041 public final static Logger LOGGER = LoggerFactory.getLogger(SeleniumTestCase.class); 042 043 /** 044 * 15 seconds 045 */ 046 public static final String PAGE_LOAD_TIMEOUT = "15000"; 047 048 public static final String TOMCAT_6 = "tomcat6"; 049 050 public static final String JETTY_7 = "jetty7"; 051 052 /** 053 * An XPath expression for locating a submit element (very commonly used 054 * with {@link #clickAndWait(String)}. 055 * 056 * @since 5.3 057 */ 058 public static final String SUBMIT = "//input[@type='submit']"; 059 060 /** 061 * The underlying {@link Selenium} instance that all the methods of this class delegate to; 062 * this can be useful when attempting to use SeleniumTestCase with a newer version of Selenium which 063 * has added some methods to the interface. This field will not be set until the test case instance 064 * has gone through its full initialization. 065 * 066 * @since 5.3 067 */ 068 protected Selenium selenium; 069 070 private String baseURL; 071 072 private ErrorReporter errorReporter; 073 074 private ITestContext testContext; 075 076 /** 077 * Starts up the servers for the entire test (i.e., for multiple TestCases). By placing <parameter> elements 078 * inside the appropriate <test> (of your testng.xml configuration 079 * file), you can change the configuration or behavior of the servers. It is common to have two 080 * or more identical tests that differ only in terms of the <code>tapestry.browser-start-command</code> parameter, 081 * to run tests against multiple browsers. 082 * <table> 083 * <tr> 084 * <th>Parameter</th> 085 * <th>Name</th> 086 * <th>Default</th> 087 * <th>Description</th> 088 * </tr> 089 * <tr> 090 * <td>container</td> 091 * <td>tapestry.servlet-container</td> 092 * <td>JETTY_7</td> 093 * <td>The Servlet container to use for the tests. Currently {@link #JETTY_7} or {@link #TOMCAT_6}</td> 094 * </tr> 095 * <tr> 096 * <td>webAppFolder</td> 097 * <td>tapestry.web-app-folder</td> 098 * <td>src/main/webapp</td> 099 * <td>Location of web application context</td> 100 * </tr> 101 * <tr> 102 * <td>contextPath</td> 103 * <td>tapestry.context-path</td> 104 * <td><em>empty string</em></td> 105 * <td>Context path (defaults to root). As elsewhere, the context path should be blank, or start with a slash (but 106 * not end with one).</td> 107 * </tr> 108 * <tr> 109 * <td>port</td> 110 * <td>tapestry.port</td> 111 * <td>9090</td> 112 * <td>Port number for web server to listen to</td> 113 * </tr> 114 * <tr> 115 * <td>sslPort</td> 116 * <td>tapestry.ssl-port</td> 117 * <td>8443</td> 118 * <td>Port number for web server to listen to for secure requests</td> 119 * </tr> 120 * <tr> 121 * <td>browserStartCommand</td> 122 * <td>tapestry.browser-start-command</td> 123 * <td>*firefox</td> 124 * <td>Command string used to launch the browser, as defined by Selenium</td> 125 * </tr> 126 * </table> 127 * <p/> 128 * Tests in the <em>beforeStartup</em> group will be run before the start of Selenium. This can be used to 129 * programmatically override the above parameter values. 130 * <p/> 131 * This method will be invoked in <em>each</em> subclass, but is set up to only startup the servers once (it checks 132 * the {@link ITestContext} to see if the necessary keys are already present). 133 * 134 * @param testContext 135 * Used to share objects between the launcher and the test suites 136 * @throws Exception 137 */ 138 @BeforeTest(dependsOnGroups = 139 {"beforeStartup"}) 140 public void testStartup(final ITestContext testContext, XmlTest xmlTest) throws Exception 141 { 142 // This is not actually necessary, because TestNG will only invoke this method once 143 // even when multiple test cases within the test extend from SeleniumTestCase. TestNG 144 // just invokes it on the "first" TestCase instance it has test methods for. 145 146 if (testContext.getAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE) != null) 147 { 148 return; 149 } 150 151 // If a parameter is overridden in another test method, TestNG won't pass the 152 // updated value via a parameter, but still passes the original (coming from testng.xml or the default). 153 // Seems like a TestNG bug. 154 155 // Map<String, String> testParameters = xmlTest.getParameters(); 156 157 TapestryTestConfiguration annotation = this.getClass().getAnnotation(TapestryTestConfiguration.class); 158 if (annotation == null) 159 { 160 @TapestryTestConfiguration 161 final class EmptyInnerClass 162 { 163 } 164 165 annotation = EmptyInnerClass.class.getAnnotation(TapestryTestConfiguration.class); 166 } 167 168 String webAppFolder = getParameter(xmlTest, TapestryTestConstants.WEB_APP_FOLDER_PARAMETER, 169 annotation.webAppFolder()); 170 String container = getParameter(xmlTest, TapestryTestConstants.SERVLET_CONTAINER_PARAMETER, 171 annotation.container()); 172 String contextPath = getParameter(xmlTest, TapestryTestConstants.CONTEXT_PATH_PARAMETER, 173 annotation.contextPath()); 174 int port = getIntParameter(xmlTest, TapestryTestConstants.PORT_PARAMETER, annotation.port()); 175 int sslPort = getIntParameter(xmlTest, TapestryTestConstants.SSL_PORT_PARAMETER, annotation.sslPort()); 176 String browserStartCommand = getParameter(xmlTest, TapestryTestConstants.BROWSER_START_COMMAND_PARAMETER, 177 annotation.browserStartCommand()); 178 179 String baseURL = String.format("http://localhost:%d%s/", port, contextPath); 180 181 String sep = System.getProperty("line.separator"); 182 183 LOGGER.info("Starting SeleniumTestCase:" + sep + 184 " currentDir: " + System.getProperty("user.dir") + sep + 185 " webAppFolder: " + webAppFolder + sep + 186 " container: " + container + sep + 187 " contextPath: " + contextPath + sep + 188 String.format(" ports: %d / %d", port, sslPort) + sep + 189 " browserStart: " + browserStartCommand + sep + 190 " baseURL: " + baseURL); 191 192 final Runnable stopWebServer = launchWebServer(container, webAppFolder, contextPath, port, sslPort); 193 194 final SeleniumServer seleniumServer = new SeleniumServer(); 195 196 File ffProfileTemplate = new File(TapestryRunnerConstants.MODULE_BASE_DIR, "src/test/conf/ff_profile_template"); 197 198 if (ffProfileTemplate.isDirectory()) 199 { 200 seleniumServer.getConfiguration().setFirefoxProfileTemplate(ffProfileTemplate); 201 } 202 203 seleniumServer.start(); 204 205 206 CommandProcessor httpCommandProcessor = new HttpCommandProcessor("localhost", 207 seleniumServer.getPort(), browserStartCommand, baseURL); 208 209 final ErrorReporterImpl errorReporter = new ErrorReporterImpl(httpCommandProcessor, testContext); 210 211 ErrorReportingCommandProcessor commandProcessor = new ErrorReportingCommandProcessor(httpCommandProcessor, 212 errorReporter); 213 214 final Selenium selenium = new DefaultSelenium(commandProcessor); 215 216 selenium.start(); 217 218 testContext.setAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE, baseURL); 219 testContext.setAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE, selenium); 220 testContext.setAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE, errorReporter); 221 testContext.setAttribute(TapestryTestConstants.COMMAND_PROCESSOR_ATTRIBUTE, commandProcessor); 222 223 testContext.setAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE, new Runnable() 224 { 225 public void run() 226 { 227 try 228 { 229 LOGGER.info("Shutting down selenium client ..."); 230 231 try 232 { 233 selenium.stop(); 234 } catch (RuntimeException e) 235 { 236 LOGGER.error("Selenium client shutdown failure.", e); 237 } 238 239 LOGGER.info("Shutting down selenium server ..."); 240 241 try 242 { 243 seleniumServer.stop(); 244 } catch (RuntimeException e) 245 { 246 LOGGER.error("Selenium server shutdown failure.", e); 247 } 248 249 LOGGER.info("Shutting web server ..."); 250 251 try 252 { 253 stopWebServer.run(); 254 } catch (RuntimeException e) 255 { 256 LOGGER.error("Web server shutdown failure.", e); 257 } 258 259 // Output, at the end of the Test, any html capture or screen shots (this makes it much easier 260 // to locate them at the end of the run; there's such a variance on where they end up based 261 // on whether the tests are running from inside an IDE or via one of the command line 262 // builds. 263 264 errorReporter.writeOutputPaths(); 265 } finally 266 { 267 testContext.removeAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE); 268 testContext.removeAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE); 269 testContext.removeAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE); 270 testContext.removeAttribute(TapestryTestConstants.COMMAND_PROCESSOR_ATTRIBUTE); 271 testContext.removeAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE); 272 } 273 } 274 }); 275 } 276 277 private final String getParameter(XmlTest xmlTest, String key, String defaultValue) 278 { 279 String value = xmlTest.getParameter(key); 280 281 return value != null ? value : defaultValue; 282 } 283 284 private final int getIntParameter(XmlTest xmlTest, String key, int defaultValue) 285 { 286 String value = xmlTest.getParameter(key); 287 288 return value != null ? Integer.parseInt(value) : defaultValue; 289 } 290 291 /** 292 * Like {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)} , this may 293 * be called multiple times against multiple instances, but only does work the first time. 294 */ 295 @AfterTest 296 public void testShutdown(ITestContext context) 297 { 298 // Likewise, this method should only be invoked once. 299 Runnable r = (Runnable) context.getAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE); 300 301 // This test is still useful, however, because testStartup() may not have completed properly, 302 // and the runnable is the last thing it puts into the test context. 303 304 if (r != null) 305 { 306 LOGGER.info("Shutting down integration test support ..."); 307 r.run(); 308 } 309 } 310 311 /** 312 * Invoked from {@link #testStartup(org.testng.ITestContext, org.testng.xml.XmlTest)} to launch the web 313 * server to be tested. The return value is a Runnable that can be invoked later to cleanly shut down the launched 314 * server at the end of the test. 315 * 316 * @param container 317 * identifies which web server should be launched 318 * @param webAppFolder 319 * path to the web application context 320 * @param contextPath 321 * the path the context is mapped to, usually the empty string 322 * @param port 323 * the port number the server should handle 324 * @param sslPort 325 * the port number on which the server should handle secure requests 326 * @return Runnable used to shut down the server 327 * @throws Exception 328 */ 329 protected Runnable launchWebServer(String container, String webAppFolder, String contextPath, int port, int sslPort) 330 throws Exception 331 { 332 final ServletContainerRunner runner = createWebServer(container, webAppFolder, contextPath, port, sslPort); 333 334 return new Runnable() 335 { 336 public void run() 337 { 338 runner.stop(); 339 } 340 }; 341 } 342 343 private ServletContainerRunner createWebServer(String container, String webAppFolder, String contextPath, int port, int sslPort) throws Exception 344 { 345 if (TOMCAT_6.equals(container)) 346 { 347 return new Tomcat6Runner(webAppFolder, contextPath, port, sslPort); 348 } 349 350 if (JETTY_7.equals(container)) 351 { 352 return new Jetty7Runner(webAppFolder, contextPath, port, sslPort); 353 } 354 355 throw new RuntimeException("Unknown servlet container: " + container); 356 } 357 358 @BeforeClass 359 public void setup(ITestContext context) 360 { 361 this.testContext = context; 362 363 selenium = (Selenium) context.getAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE); 364 baseURL = (String) context.getAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE); 365 errorReporter = (ErrorReporter) context.getAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE); 366 } 367 368 @AfterClass 369 public void cleanup() 370 { 371 selenium = null; 372 baseURL = null; 373 errorReporter = null; 374 testContext = null; 375 } 376 377 /** 378 * Delegates to {@link ErrorReporter#writeErrorReport(String)} to capture the current page markup in a 379 * file for later analysis. 380 */ 381 protected void writeErrorReport(String reportText) 382 { 383 errorReporter.writeErrorReport(reportText); 384 } 385 386 /** 387 * Returns the base URL for the application. This is of the typically <code>http://localhost:9999/</code> (i.e., it 388 * includes a trailing slash). 389 * <p/> 390 * Generally, you should use {@link #openLinks(String...)} to start from your application's home page. 391 */ 392 public String getBaseURL() 393 { 394 return baseURL; 395 } 396 397 @BeforeMethod 398 public void indicateTestMethodName(Method testMethod) 399 { 400 LOGGER.info("Executing " + testMethod); 401 402 testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, testMethod); 403 404 String className = testMethod.getDeclaringClass().getSimpleName(); 405 String testName = testMethod.getName().replace("_", " "); 406 407 selenium.setContext(className + ": " + testName); 408 } 409 410 @AfterMethod 411 public void cleanupTestMethod() 412 { 413 testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, null); 414 } 415 416 // --------------------------------------------------------------------- 417 // Start of delegate methods 418 // 419 // When upgrading to a new version of Selenium, it is probably easiest 420 // to delete all these methods and use the Generate Delegate Methods 421 // refactoring. 422 // --------------------------------------------------------------------- 423 424 public void addCustomRequestHeader(String key, String value) 425 { 426 selenium.addCustomRequestHeader(key, value); 427 } 428 429 public void addLocationStrategy(String strategyName, String functionDefinition) 430 { 431 selenium.addLocationStrategy(strategyName, functionDefinition); 432 } 433 434 public void addScript(String scriptContent, String scriptTagId) 435 { 436 selenium.addScript(scriptContent, scriptTagId); 437 } 438 439 public void addSelection(String locator, String optionLocator) 440 { 441 selenium.addSelection(locator, optionLocator); 442 } 443 444 public void allowNativeXpath(String allow) 445 { 446 selenium.allowNativeXpath(allow); 447 } 448 449 public void altKeyDown() 450 { 451 selenium.altKeyDown(); 452 } 453 454 public void altKeyUp() 455 { 456 selenium.altKeyUp(); 457 } 458 459 public void answerOnNextPrompt(String answer) 460 { 461 selenium.answerOnNextPrompt(answer); 462 } 463 464 public void assignId(String locator, String identifier) 465 { 466 selenium.assignId(locator, identifier); 467 } 468 469 public void attachFile(String fieldLocator, String fileLocator) 470 { 471 selenium.attachFile(fieldLocator, fileLocator); 472 } 473 474 public void captureEntirePageScreenshot(String filename, String kwargs) 475 { 476 selenium.captureEntirePageScreenshot(filename, kwargs); 477 } 478 479 public String captureEntirePageScreenshotToString(String kwargs) 480 { 481 return selenium.captureEntirePageScreenshotToString(kwargs); 482 } 483 484 public String captureNetworkTraffic(String type) 485 { 486 return selenium.captureNetworkTraffic(type); 487 } 488 489 public void captureScreenshot(String filename) 490 { 491 selenium.captureScreenshot(filename); 492 } 493 494 public String captureScreenshotToString() 495 { 496 return selenium.captureScreenshotToString(); 497 } 498 499 public void check(String locator) 500 { 501 selenium.check(locator); 502 } 503 504 public void chooseCancelOnNextConfirmation() 505 { 506 selenium.chooseCancelOnNextConfirmation(); 507 } 508 509 public void chooseOkOnNextConfirmation() 510 { 511 selenium.chooseOkOnNextConfirmation(); 512 } 513 514 public void click(String locator) 515 { 516 selenium.click(locator); 517 } 518 519 public void clickAt(String locator, String coordString) 520 { 521 selenium.clickAt(locator, coordString); 522 } 523 524 public void close() 525 { 526 selenium.close(); 527 } 528 529 public void contextMenu(String locator) 530 { 531 selenium.contextMenu(locator); 532 } 533 534 public void contextMenuAt(String locator, String coordString) 535 { 536 selenium.contextMenuAt(locator, coordString); 537 } 538 539 public void controlKeyDown() 540 { 541 selenium.controlKeyDown(); 542 } 543 544 public void controlKeyUp() 545 { 546 selenium.controlKeyUp(); 547 } 548 549 public void createCookie(String nameValuePair, String optionsString) 550 { 551 selenium.createCookie(nameValuePair, optionsString); 552 } 553 554 public void deleteAllVisibleCookies() 555 { 556 selenium.deleteAllVisibleCookies(); 557 } 558 559 public void deleteCookie(String name, String optionsString) 560 { 561 selenium.deleteCookie(name, optionsString); 562 } 563 564 public void deselectPopUp() 565 { 566 selenium.deselectPopUp(); 567 } 568 569 public void doubleClick(String locator) 570 { 571 selenium.doubleClick(locator); 572 } 573 574 public void doubleClickAt(String locator, String coordString) 575 { 576 selenium.doubleClickAt(locator, coordString); 577 } 578 579 public void dragAndDrop(String locator, String movementsString) 580 { 581 selenium.dragAndDrop(locator, movementsString); 582 } 583 584 public void dragAndDropToObject(String locatorOfObjectToBeDragged, String locatorOfDragDestinationObject) 585 { 586 selenium.dragAndDropToObject(locatorOfObjectToBeDragged, locatorOfDragDestinationObject); 587 } 588 589 public void dragdrop(String locator, String movementsString) 590 { 591 selenium.dragdrop(locator, movementsString); 592 } 593 594 public void fireEvent(String locator, String eventName) 595 { 596 selenium.fireEvent(locator, eventName); 597 } 598 599 public void focus(String locator) 600 { 601 selenium.focus(locator); 602 } 603 604 public String getAlert() 605 { 606 return selenium.getAlert(); 607 } 608 609 public String[] getAllButtons() 610 { 611 return selenium.getAllButtons(); 612 } 613 614 public String[] getAllFields() 615 { 616 return selenium.getAllFields(); 617 } 618 619 public String[] getAllLinks() 620 { 621 return selenium.getAllLinks(); 622 } 623 624 public String[] getAllWindowIds() 625 { 626 return selenium.getAllWindowIds(); 627 } 628 629 public String[] getAllWindowNames() 630 { 631 return selenium.getAllWindowNames(); 632 } 633 634 public String[] getAllWindowTitles() 635 { 636 return selenium.getAllWindowTitles(); 637 } 638 639 public String getAttribute(String attributeLocator) 640 { 641 return selenium.getAttribute(attributeLocator); 642 } 643 644 public String[] getAttributeFromAllWindows(String attributeName) 645 { 646 return selenium.getAttributeFromAllWindows(attributeName); 647 } 648 649 public String getBodyText() 650 { 651 return selenium.getBodyText(); 652 } 653 654 public String getConfirmation() 655 { 656 return selenium.getConfirmation(); 657 } 658 659 public String getCookie() 660 { 661 return selenium.getCookie(); 662 } 663 664 public String getCookieByName(String name) 665 { 666 return selenium.getCookieByName(name); 667 } 668 669 public Number getCursorPosition(String locator) 670 { 671 return selenium.getCursorPosition(locator); 672 } 673 674 public Number getElementHeight(String locator) 675 { 676 return selenium.getElementHeight(locator); 677 } 678 679 public Number getElementIndex(String locator) 680 { 681 return selenium.getElementIndex(locator); 682 } 683 684 public Number getElementPositionLeft(String locator) 685 { 686 return selenium.getElementPositionLeft(locator); 687 } 688 689 public Number getElementPositionTop(String locator) 690 { 691 return selenium.getElementPositionTop(locator); 692 } 693 694 public Number getElementWidth(String locator) 695 { 696 return selenium.getElementWidth(locator); 697 } 698 699 public String getEval(String script) 700 { 701 return selenium.getEval(script); 702 } 703 704 public String getExpression(String expression) 705 { 706 return selenium.getExpression(expression); 707 } 708 709 public String getHtmlSource() 710 { 711 return selenium.getHtmlSource(); 712 } 713 714 public String getLocation() 715 { 716 return selenium.getLocation(); 717 } 718 719 public String getLog() 720 { 721 return selenium.getLog(); 722 } 723 724 public Number getMouseSpeed() 725 { 726 return selenium.getMouseSpeed(); 727 } 728 729 public String getPrompt() 730 { 731 return selenium.getPrompt(); 732 } 733 734 public String getSelectedId(String selectLocator) 735 { 736 return selenium.getSelectedId(selectLocator); 737 } 738 739 public String[] getSelectedIds(String selectLocator) 740 { 741 return selenium.getSelectedIds(selectLocator); 742 } 743 744 public String getSelectedIndex(String selectLocator) 745 { 746 return selenium.getSelectedIndex(selectLocator); 747 } 748 749 public String[] getSelectedIndexes(String selectLocator) 750 { 751 return selenium.getSelectedIndexes(selectLocator); 752 } 753 754 public String getSelectedLabel(String selectLocator) 755 { 756 return selenium.getSelectedLabel(selectLocator); 757 } 758 759 public String[] getSelectedLabels(String selectLocator) 760 { 761 return selenium.getSelectedLabels(selectLocator); 762 } 763 764 public String getSelectedValue(String selectLocator) 765 { 766 return selenium.getSelectedValue(selectLocator); 767 } 768 769 public String[] getSelectedValues(String selectLocator) 770 { 771 return selenium.getSelectedValues(selectLocator); 772 } 773 774 public String[] getSelectOptions(String selectLocator) 775 { 776 return selenium.getSelectOptions(selectLocator); 777 } 778 779 public String getSpeed() 780 { 781 return selenium.getSpeed(); 782 } 783 784 public String getTable(String tableCellAddress) 785 { 786 return selenium.getTable(tableCellAddress); 787 } 788 789 public String getText(String locator) 790 { 791 return selenium.getText(locator); 792 } 793 794 public String getTitle() 795 { 796 return selenium.getTitle(); 797 } 798 799 public String getValue(String locator) 800 { 801 return selenium.getValue(locator); 802 } 803 804 public boolean getWhetherThisFrameMatchFrameExpression(String currentFrameString, String target) 805 { 806 return selenium.getWhetherThisFrameMatchFrameExpression(currentFrameString, target); 807 } 808 809 public boolean getWhetherThisWindowMatchWindowExpression(String currentWindowString, String target) 810 { 811 return selenium.getWhetherThisWindowMatchWindowExpression(currentWindowString, target); 812 } 813 814 public Number getXpathCount(String xpath) 815 { 816 return selenium.getXpathCount(xpath); 817 } 818 819 public void goBack() 820 { 821 selenium.goBack(); 822 } 823 824 public void highlight(String locator) 825 { 826 selenium.highlight(locator); 827 } 828 829 public void ignoreAttributesWithoutValue(String ignore) 830 { 831 selenium.ignoreAttributesWithoutValue(ignore); 832 } 833 834 public boolean isAlertPresent() 835 { 836 return selenium.isAlertPresent(); 837 } 838 839 public boolean isChecked(String locator) 840 { 841 return selenium.isChecked(locator); 842 } 843 844 public boolean isConfirmationPresent() 845 { 846 return selenium.isConfirmationPresent(); 847 } 848 849 public boolean isCookiePresent(String name) 850 { 851 return selenium.isCookiePresent(name); 852 } 853 854 public boolean isEditable(String locator) 855 { 856 return selenium.isEditable(locator); 857 } 858 859 public boolean isElementPresent(String locator) 860 { 861 return selenium.isElementPresent(locator); 862 } 863 864 public boolean isOrdered(String locator1, String locator2) 865 { 866 return selenium.isOrdered(locator1, locator2); 867 } 868 869 public boolean isPromptPresent() 870 { 871 return selenium.isPromptPresent(); 872 } 873 874 public boolean isSomethingSelected(String selectLocator) 875 { 876 return selenium.isSomethingSelected(selectLocator); 877 } 878 879 public boolean isTextPresent(String pattern) 880 { 881 return selenium.isTextPresent(pattern); 882 } 883 884 public boolean isVisible(String locator) 885 { 886 return selenium.isVisible(locator); 887 } 888 889 public void keyDown(String locator, String keySequence) 890 { 891 selenium.keyDown(locator, keySequence); 892 } 893 894 public void keyDownNative(String keycode) 895 { 896 selenium.keyDownNative(keycode); 897 } 898 899 public void keyPress(String locator, String keySequence) 900 { 901 selenium.keyPress(locator, keySequence); 902 } 903 904 public void keyPressNative(String keycode) 905 { 906 selenium.keyPressNative(keycode); 907 } 908 909 public void keyUp(String locator, String keySequence) 910 { 911 selenium.keyUp(locator, keySequence); 912 } 913 914 public void keyUpNative(String keycode) 915 { 916 selenium.keyUpNative(keycode); 917 } 918 919 public void metaKeyDown() 920 { 921 selenium.metaKeyDown(); 922 } 923 924 public void metaKeyUp() 925 { 926 selenium.metaKeyUp(); 927 } 928 929 public void mouseDown(String locator) 930 { 931 selenium.mouseDown(locator); 932 } 933 934 public void mouseDownAt(String locator, String coordString) 935 { 936 selenium.mouseDownAt(locator, coordString); 937 } 938 939 public void mouseDownRight(String locator) 940 { 941 selenium.mouseDownRight(locator); 942 } 943 944 public void mouseDownRightAt(String locator, String coordString) 945 { 946 selenium.mouseDownRightAt(locator, coordString); 947 } 948 949 public void mouseMove(String locator) 950 { 951 selenium.mouseMove(locator); 952 } 953 954 public void mouseMoveAt(String locator, String coordString) 955 { 956 selenium.mouseMoveAt(locator, coordString); 957 } 958 959 public void mouseOut(String locator) 960 { 961 selenium.mouseOut(locator); 962 } 963 964 public void mouseOver(String locator) 965 { 966 selenium.mouseOver(locator); 967 } 968 969 public void mouseUp(String locator) 970 { 971 selenium.mouseUp(locator); 972 } 973 974 public void mouseUpAt(String locator, String coordString) 975 { 976 selenium.mouseUpAt(locator, coordString); 977 } 978 979 public void mouseUpRight(String locator) 980 { 981 selenium.mouseUpRight(locator); 982 } 983 984 public void mouseUpRightAt(String locator, String coordString) 985 { 986 selenium.mouseUpRightAt(locator, coordString); 987 } 988 989 public void open(String url) 990 { 991 selenium.open(url); 992 } 993 994 public void open(String url, String ignoreResponseCode) 995 { 996 selenium.open(url, ignoreResponseCode); 997 } 998 999 public void openWindow(String url, String windowID) 1000 { 1001 selenium.openWindow(url, windowID); 1002 } 1003 1004 public void refresh() 1005 { 1006 selenium.refresh(); 1007 } 1008 1009 public void removeAllSelections(String locator) 1010 { 1011 selenium.removeAllSelections(locator); 1012 } 1013 1014 public void removeScript(String scriptTagId) 1015 { 1016 selenium.removeScript(scriptTagId); 1017 } 1018 1019 public void removeSelection(String locator, String optionLocator) 1020 { 1021 selenium.removeSelection(locator, optionLocator); 1022 } 1023 1024 public String retrieveLastRemoteControlLogs() 1025 { 1026 return selenium.retrieveLastRemoteControlLogs(); 1027 } 1028 1029 public void rollup(String rollupName, String kwargs) 1030 { 1031 selenium.rollup(rollupName, kwargs); 1032 } 1033 1034 public void runScript(String script) 1035 { 1036 selenium.runScript(script); 1037 } 1038 1039 public void select(String selectLocator, String optionLocator) 1040 { 1041 selenium.select(selectLocator, optionLocator); 1042 } 1043 1044 public void selectFrame(String locator) 1045 { 1046 selenium.selectFrame(locator); 1047 } 1048 1049 public void selectPopUp(String windowID) 1050 { 1051 selenium.selectPopUp(windowID); 1052 } 1053 1054 public void selectWindow(String windowID) 1055 { 1056 selenium.selectWindow(windowID); 1057 } 1058 1059 public void setBrowserLogLevel(String logLevel) 1060 { 1061 selenium.setBrowserLogLevel(logLevel); 1062 } 1063 1064 public void setContext(String context) 1065 { 1066 selenium.setContext(context); 1067 } 1068 1069 public void setCursorPosition(String locator, String position) 1070 { 1071 selenium.setCursorPosition(locator, position); 1072 } 1073 1074 public void setExtensionJs(String extensionJs) 1075 { 1076 selenium.setExtensionJs(extensionJs); 1077 } 1078 1079 public void setMouseSpeed(String pixels) 1080 { 1081 selenium.setMouseSpeed(pixels); 1082 } 1083 1084 public void setSpeed(String value) 1085 { 1086 selenium.setSpeed(value); 1087 } 1088 1089 public void setTimeout(String timeout) 1090 { 1091 selenium.setTimeout(timeout); 1092 } 1093 1094 public void shiftKeyDown() 1095 { 1096 selenium.shiftKeyDown(); 1097 } 1098 1099 public void shiftKeyUp() 1100 { 1101 selenium.shiftKeyUp(); 1102 } 1103 1104 public void showContextualBanner() 1105 { 1106 selenium.showContextualBanner(); 1107 } 1108 1109 public void showContextualBanner(String className, String methodName) 1110 { 1111 selenium.showContextualBanner(className, methodName); 1112 } 1113 1114 public void shutDownSeleniumServer() 1115 { 1116 selenium.shutDownSeleniumServer(); 1117 } 1118 1119 public void start() 1120 { 1121 selenium.start(); 1122 } 1123 1124 public void start(Object optionsObject) 1125 { 1126 selenium.start(optionsObject); 1127 } 1128 1129 public void start(String optionsString) 1130 { 1131 selenium.start(optionsString); 1132 } 1133 1134 public void stop() 1135 { 1136 selenium.stop(); 1137 } 1138 1139 public void submit(String formLocator) 1140 { 1141 selenium.submit(formLocator); 1142 } 1143 1144 public void type(String locator, String value) 1145 { 1146 selenium.type(locator, value); 1147 } 1148 1149 public void typeKeys(String locator, String value) 1150 { 1151 selenium.typeKeys(locator, value); 1152 } 1153 1154 public void uncheck(String locator) 1155 { 1156 selenium.uncheck(locator); 1157 } 1158 1159 public void useXpathLibrary(String libraryName) 1160 { 1161 selenium.useXpathLibrary(libraryName); 1162 } 1163 1164 public void waitForCondition(String script, String timeout) 1165 { 1166 selenium.waitForCondition(script, timeout); 1167 } 1168 1169 public void waitForFrameToLoad(String frameAddress, String timeout) 1170 { 1171 selenium.waitForFrameToLoad(frameAddress, timeout); 1172 } 1173 1174 /** 1175 * Waits for page to load, then waits for initialization to finish, which is recognized by the {@code data-page-initialized} attribute 1176 * being set to true on the body element. Polls at increasing intervals, for up-to 30 seconds (that's extraordinarily long, but helps sometimes 1177 * when manually debugging a page that doesn't have the floating console enabled).. 1178 */ 1179 public void waitForPageToLoad(String timeout) 1180 { 1181 selenium.waitForPageToLoad(timeout); 1182 1183 // In a limited number of cases, a "page" is an container error page or raw HTML content 1184 // that does not include the body element and data-page-initialized element. In those cases, 1185 // there will never be page initialization in the Tapestry sense and we return immediately. 1186 1187 if (!isElementPresent("css=body[data-page-initialized]")) 1188 { 1189 return; 1190 } 1191 1192 final long pollingStartTime = System.currentTimeMillis(); 1193 1194 long sleepTime = 20; 1195 1196 while (true) 1197 { 1198 if (isElementPresent("css=body[data-page-initialized='true']")) 1199 { 1200 return; 1201 } 1202 1203 if ((System.currentTimeMillis() - pollingStartTime) > 30000) 1204 { 1205 reportAndThrowAssertionError("Page did not finish initializing after 30 seconds."); 1206 } 1207 1208 sleep(sleepTime); 1209 1210 sleepTime *= 2; 1211 } 1212 } 1213 1214 public void waitForPopUp(String windowID, String timeout) 1215 { 1216 selenium.waitForPopUp(windowID, timeout); 1217 } 1218 1219 public void windowFocus() 1220 { 1221 selenium.windowFocus(); 1222 } 1223 1224 public void windowMaximize() 1225 { 1226 selenium.windowMaximize(); 1227 } 1228 1229 // --------------------------------------------------------------------- 1230 // End of delegate methods 1231 // --------------------------------------------------------------------- 1232 1233 /** 1234 * Formats a message from the provided arguments, which is written to System.err. In addition, 1235 * captures the AUT's markup, screenshot, and a report to the output directory. 1236 * 1237 * @param message 1238 * @param arguments 1239 * @since 5.4 1240 */ 1241 protected final void reportAndThrowAssertionError(String message, Object... arguments) 1242 { 1243 StringBuilder builder = new StringBuilder(5000); 1244 1245 String formatted = String.format(message, arguments); 1246 1247 builder.append(formatted); 1248 1249 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 1250 1251 StringBuilder buffer = new StringBuilder(5000); 1252 1253 boolean enabled = false; 1254 1255 for (StackTraceElement e : stackTrace) 1256 { 1257 if (enabled) 1258 { 1259 buffer.append("\n- "); 1260 buffer.append(e); 1261 continue; 1262 } 1263 1264 if (e.getMethodName().equals("reportAndThrowAssertionError")) 1265 { 1266 enabled = true; 1267 } 1268 } 1269 1270 writeErrorReport(builder.toString()); 1271 1272 throw new AssertionError(formatted); 1273 } 1274 1275 protected final void unreachable() 1276 { 1277 reportAndThrowAssertionError("An unreachable statement was reached."); 1278 } 1279 1280 /** 1281 * Open the {@linkplain #getBaseURL()}, and waits for the page to load. 1282 */ 1283 protected final void openBaseURL() 1284 { 1285 open(baseURL); 1286 1287 waitForPageToLoad(); 1288 } 1289 1290 /** 1291 * Asserts the text of an element, identified by the locator. 1292 * 1293 * @param locator 1294 * identifies the element whose text value is to be asserted 1295 * @param expected 1296 * expected value for the element's text 1297 */ 1298 protected final void assertText(String locator, String expected) 1299 { 1300 String actual = null; 1301 1302 try 1303 { 1304 actual = getText(locator); 1305 } catch (RuntimeException ex) 1306 { 1307 System.err.printf("Error accessing %s: %s, in:\n\n%s\n\n", locator, ex.getMessage(), getHtmlSource()); 1308 1309 throw ex; 1310 } 1311 1312 if (actual.equals(expected)) 1313 { 1314 return; 1315 } 1316 1317 reportAndThrowAssertionError("%s was '%s' not '%s'", locator, actual, expected); 1318 } 1319 1320 protected final void assertTextPresent(String... text) 1321 { 1322 for (String item : text) 1323 { 1324 if (isTextPresent(item)) 1325 { 1326 continue; 1327 } 1328 1329 reportAndThrowAssertionError("Page did not contain '" + item + "'."); 1330 } 1331 } 1332 1333 /** 1334 * Assets that each string provided is present somewhere in the current document. 1335 * 1336 * @param expected 1337 * string expected to be present 1338 */ 1339 protected final void assertSourcePresent(String... expected) 1340 { 1341 String source = getHtmlSource(); 1342 1343 for (String snippet : expected) 1344 { 1345 if (source.contains(snippet)) 1346 { 1347 continue; 1348 } 1349 1350 reportAndThrowAssertionError("Page did not contain source '" + snippet + "'."); 1351 } 1352 } 1353 1354 /** 1355 * Click a link identified by a locator, then wait for the resulting page to load. 1356 * This is not useful for Ajax updates, just normal full-page refreshes. 1357 * 1358 * @param locator 1359 * identifies the link to click 1360 */ 1361 protected final void clickAndWait(String locator) 1362 { 1363 click(locator); 1364 1365 waitForPageToLoad(); 1366 } 1367 1368 /** 1369 * Waits for the page to load (up to 15 seconds). This is invoked after clicking on an element 1370 * that forces a full page refresh. 1371 */ 1372 protected final void waitForPageToLoad() 1373 { 1374 waitForPageToLoad(PAGE_LOAD_TIMEOUT); 1375 } 1376 1377 /** 1378 * Used when the locator identifies an attribute, not an element. 1379 * 1380 * @param locator 1381 * identifies the attribute whose value is to be asserted 1382 * @param expected 1383 * expected value for the attribute 1384 */ 1385 protected final void assertAttribute(String locator, String expected) 1386 { 1387 String actual = null; 1388 1389 try 1390 { 1391 actual = getAttribute(locator); 1392 } catch (RuntimeException ex) 1393 { 1394 1395 reportAndThrowAssertionError("Error accessing %s: %s", locator, ex.getMessage()); 1396 } 1397 1398 if (actual.equals(expected)) 1399 { 1400 return; 1401 } 1402 1403 reportAndThrowAssertionError("%s was '%s' not '%s'", locator, actual, expected); 1404 } 1405 1406 /** 1407 * Assets that the value in the field matches the expectation 1408 * 1409 * @param locator 1410 * identifies the field 1411 * @param expected 1412 * expected value for the field 1413 * @since 5.3 1414 */ 1415 protected final void assertFieldValue(String locator, String expected) 1416 { 1417 try 1418 { 1419 assertEquals(getValue(locator), expected); 1420 } catch (AssertionError ex) 1421 { 1422 reportAndThrowAssertionError("Failure accessing %s: %s", locator, ex); 1423 } 1424 } 1425 1426 /** 1427 * Opens the base URL, then clicks through a series of links to get to a desired application 1428 * state. 1429 * 1430 * @since 5.3 1431 */ 1432 protected final void openLinks(String... linkText) 1433 { 1434 openBaseURL(); 1435 1436 for (String text : linkText) 1437 { 1438 clickAndWait("link=" + text); 1439 } 1440 } 1441 1442 /** 1443 * Sleeps for the indicated number of seconds. 1444 * 1445 * @since 5.3 1446 */ 1447 protected final void sleep(long millis) 1448 { 1449 try 1450 { 1451 Thread.sleep(millis); 1452 } catch (InterruptedException ex) 1453 { 1454 // Ignore. 1455 } 1456 } 1457 1458 /** 1459 * Waits, up to the page load limit for an element (identified by a CSS rule) to exist 1460 * (it is not assured that the element will be visible). 1461 * <p/> 1462 * This implementation only works if the application provides a function onto the 1463 * window object: "testSupport.findCSSMatchCount()" which accepts a CSS rule and returns the number 1464 * of matching elements. 1465 * 1466 * @param cssSelector 1467 * used to locate the element 1468 * @since 5.3 1469 * @deprecated Deprecated in 5.4 with no replacement 1470 */ 1471 protected void waitForCSSSelectedElementToAppear(String cssSelector) 1472 { 1473 String condition = String.format("window.testSupport.findCSSMatchCount(\"%s\") > 0", cssSelector); 1474 1475 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1476 } 1477 1478 /** 1479 * Waits for the element with the given client-side id to be present in the DOM ( 1480 * does not assure that the element is visible). 1481 * 1482 * @param elementId 1483 * identifies the element 1484 * @since 5.3 1485 */ 1486 protected final void waitForElementToAppear(String elementId) 1487 { 1488 1489 String condition = String.format("selenium.browserbot.getCurrentWindow().document.getElementById(\"%s\")", elementId); 1490 1491 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1492 } 1493 1494 /** 1495 * Waits for the element to be removed from the DOM. 1496 * <p/> 1497 * <p/> 1498 * This implementation depends on window being extended with testSupport.isNotVisible(). 1499 * 1500 * @param elementId 1501 * client-side id of element 1502 * @since 5.3 1503 * @deprecated Deprecated in 5.4 with no replacement 1504 */ 1505 protected final void waitForElementToDisappear(String elementId) 1506 { 1507 String condition = String.format("selenium.browserbot.getCurrentWindow().testSupport.doesNotExist(\"%s\")", elementId); 1508 1509 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1510 } 1511 1512 /** 1513 * Waits for the element specified by the selector to become visible 1514 * Note that waitForElementToAppear waits for the element to be present in the dom, visible or not. waitForVisible 1515 * waits for an element that already exists in the dom to become visible. 1516 * 1517 * @param selector 1518 * element selector 1519 * @since 5.3 1520 */ 1521 protected final void waitForVisible(String selector) 1522 { 1523 String condition = String.format("selenium.isVisible(\"%s\")", selector); 1524 1525 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1526 } 1527 1528 /** 1529 * Waits for the element specified by the selector to become invisible 1530 * Note that waitForElementToDisappear waits for the element to be absent from the dom, visible or not. waitForInvisible 1531 * waits for an existing element to become invisible. 1532 * 1533 * @param selector 1534 * element selector 1535 * @since 5.3 1536 */ 1537 protected final void waitForInvisible(String selector) 1538 { 1539 String condition = String.format("!selenium.isVisible(\"%s\")", selector); 1540 1541 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1542 } 1543 1544 /** 1545 * Asserts that the current page's title matches the expected value. 1546 * 1547 * @param expected 1548 * value for title 1549 * @since 5.3 1550 */ 1551 protected final void assertTitle(String expected) 1552 { 1553 try 1554 { 1555 assertEquals(getTitle(), expected); 1556 } catch (AssertionError ex) 1557 { 1558 reportAndThrowAssertionError("Unexpected title: %s", ex); 1559 1560 throw ex; 1561 } 1562 } 1563 1564 /** 1565 * Waits until all active XHR requests are completed. 1566 * 1567 * @param timeout 1568 * timeout to wait for (no longer used) 1569 * @since 5.3 1570 * @deprecated Deprecated in 5.4 in favor of the version without a timeout 1571 */ 1572 protected final void waitForAjaxRequestsToComplete(String timeout) 1573 { 1574 waitForAjaxRequestsToComplete(); 1575 } 1576 1577 1578 /** 1579 * Waits until all active XHR requests (as noted by the t5/core/dom module) 1580 * have completed. Waits up to 500 ms. 1581 * 1582 * @since 5.4 1583 */ 1584 protected final void waitForAjaxRequestsToComplete() { 1585 for (int i = 0; i < 6; i++) 1586 { 1587 if (i > 0) 1588 { 1589 sleep(100); 1590 } 1591 1592 // The t5/core/dom module tracks how many Ajax requests are active 1593 // and updates this property as appropriate. 1594 if (getAttribute("//body/@data-ajax-active").equals("false")) 1595 { 1596 return; 1597 } 1598 } 1599 1600 reportAndThrowAssertionError("Body 'data-ajax-active' attribute never reverted to 'false'."); 1601 } 1602 1603 public Number getCssCount(String str) 1604 { 1605 return selenium.getCssCount(str); 1606 } 1607 1608}