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