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 * <caption>Options and defaults</caption> 125 * </table> 126 * 127 * Tests in the <em>beforeStartup</em> group will be run before the start of Selenium. This can be used to 128 * programmatically override the above parameter values. 129 * 130 * This method will be invoked in <em>each</em> subclass, but is set up to only startup the servers once (it checks 131 * the {@link ITestContext} to see if the necessary keys are already present). 132 * 133 * @param testContext 134 * Used to share objects between the launcher and the test suites 135 * @throws Exception 136 */ 137 @BeforeTest(dependsOnGroups = 138 {"beforeStartup"}) 139 public void testStartup(final ITestContext testContext, XmlTest xmlTest) throws Exception 140 { 141 // This is not actually necessary, because TestNG will only invoke this method once 142 // even when multiple test cases within the test extend from SeleniumTestCase. TestNG 143 // just invokes it on the "first" TestCase instance it has test methods for. 144 145 if (testContext.getAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE) != null) 146 { 147 return; 148 } 149 150 // If a parameter is overridden in another test method, TestNG won't pass the 151 // updated value via a parameter, but still passes the original (coming from testng.xml or the default). 152 // Seems like a TestNG bug. 153 154 // Map<String, String> testParameters = xmlTest.getParameters(); 155 156 TapestryTestConfiguration annotation = this.getClass().getAnnotation(TapestryTestConfiguration.class); 157 if (annotation == null) 158 { 159 @TapestryTestConfiguration 160 final class EmptyInnerClass 161 { 162 } 163 164 annotation = EmptyInnerClass.class.getAnnotation(TapestryTestConfiguration.class); 165 } 166 167 String webAppFolder = getParameter(xmlTest, TapestryTestConstants.WEB_APP_FOLDER_PARAMETER, 168 annotation.webAppFolder()); 169 String container = getParameter(xmlTest, TapestryTestConstants.SERVLET_CONTAINER_PARAMETER, 170 annotation.container()); 171 String contextPath = getParameter(xmlTest, TapestryTestConstants.CONTEXT_PATH_PARAMETER, 172 annotation.contextPath()); 173 int port = getIntParameter(xmlTest, TapestryTestConstants.PORT_PARAMETER, annotation.port()); 174 int sslPort = getIntParameter(xmlTest, TapestryTestConstants.SSL_PORT_PARAMETER, annotation.sslPort()); 175 String browserStartCommand = getParameter(xmlTest, TapestryTestConstants.BROWSER_START_COMMAND_PARAMETER, 176 annotation.browserStartCommand()); 177 178 String baseURL = String.format("http://localhost:%d%s/", port, contextPath); 179 180 String sep = System.getProperty("line.separator"); 181 182 LOGGER.info("Starting SeleniumTestCase:" + sep + 183 " currentDir: " + System.getProperty("user.dir") + sep + 184 " webAppFolder: " + webAppFolder + sep + 185 " container: " + container + sep + 186 " contextPath: " + contextPath + sep + 187 String.format(" ports: %d / %d", port, sslPort) + sep + 188 " browserStart: " + browserStartCommand + sep + 189 " baseURL: " + baseURL); 190 191 final Runnable stopWebServer = launchWebServer(container, webAppFolder, contextPath, port, sslPort); 192 193 final SeleniumServer seleniumServer = new SeleniumServer(); 194 195 File ffProfileTemplate = new File(TapestryRunnerConstants.MODULE_BASE_DIR, "src/test/conf/ff_profile_template"); 196 197 if (ffProfileTemplate.isDirectory()) 198 { 199 seleniumServer.getConfiguration().setFirefoxProfileTemplate(ffProfileTemplate); 200 } 201 202 seleniumServer.start(); 203 204 205 CommandProcessor httpCommandProcessor = new HttpCommandProcessor("localhost", 206 seleniumServer.getPort(), browserStartCommand, baseURL); 207 208 final ErrorReporterImpl errorReporter = new ErrorReporterImpl(httpCommandProcessor, testContext); 209 210 ErrorReportingCommandProcessor commandProcessor = new ErrorReportingCommandProcessor(httpCommandProcessor, 211 errorReporter); 212 213 final Selenium selenium = new DefaultSelenium(commandProcessor); 214 215 selenium.start(); 216 217 testContext.setAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE, baseURL); 218 testContext.setAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE, selenium); 219 testContext.setAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE, errorReporter); 220 testContext.setAttribute(TapestryTestConstants.COMMAND_PROCESSOR_ATTRIBUTE, commandProcessor); 221 222 testContext.setAttribute(TapestryTestConstants.SHUTDOWN_ATTRIBUTE, new Runnable() 223 { 224 @Override 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 @Override 337 public void run() 338 { 339 runner.stop(); 340 } 341 }; 342 } 343 344 private ServletContainerRunner createWebServer(String container, String webAppFolder, String contextPath, int port, int sslPort) throws Exception 345 { 346 if (TOMCAT_6.equals(container)) 347 { 348 return new Tomcat6Runner(webAppFolder, contextPath, port, sslPort); 349 } 350 351 if (JETTY_7.equals(container)) 352 { 353 return new Jetty7Runner(webAppFolder, contextPath, port, sslPort); 354 } 355 356 throw new RuntimeException("Unknown servlet container: " + container); 357 } 358 359 @BeforeClass 360 public void setup(ITestContext context) 361 { 362 this.testContext = context; 363 364 selenium = (Selenium) context.getAttribute(TapestryTestConstants.SELENIUM_ATTRIBUTE); 365 baseURL = (String) context.getAttribute(TapestryTestConstants.BASE_URL_ATTRIBUTE); 366 errorReporter = (ErrorReporter) context.getAttribute(TapestryTestConstants.ERROR_REPORTER_ATTRIBUTE); 367 } 368 369 @AfterClass 370 public void cleanup() 371 { 372 selenium = null; 373 baseURL = null; 374 errorReporter = null; 375 testContext = null; 376 } 377 378 /** 379 * Delegates to {@link ErrorReporter#writeErrorReport(String)} to capture the current page markup in a 380 * file for later analysis. 381 */ 382 protected void writeErrorReport(String reportText) 383 { 384 errorReporter.writeErrorReport(reportText); 385 } 386 387 /** 388 * Returns the base URL for the application. This is of the typically <code>http://localhost:9999/</code> (i.e., it 389 * includes a trailing slash). 390 * 391 * Generally, you should use {@link #openLinks(String...)} to start from your application's home page. 392 */ 393 public String getBaseURL() 394 { 395 return baseURL; 396 } 397 398 @BeforeMethod 399 public void indicateTestMethodName(Method testMethod) 400 { 401 LOGGER.info("Executing " + testMethod); 402 403 testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, testMethod); 404 405 String className = testMethod.getDeclaringClass().getSimpleName(); 406 String testName = testMethod.getName().replace("_", " "); 407 408 selenium.setContext(className + ": " + testName); 409 } 410 411 @AfterMethod 412 public void cleanupTestMethod() 413 { 414 testContext.setAttribute(TapestryTestConstants.CURRENT_TEST_METHOD_ATTRIBUTE, null); 415 } 416 417 // --------------------------------------------------------------------- 418 // Start of delegate methods 419 // 420 // When upgrading to a new version of Selenium, it is probably easiest 421 // to delete all these methods and use the Generate Delegate Methods 422 // refactoring. 423 // --------------------------------------------------------------------- 424 425 @Override 426 public void addCustomRequestHeader(String key, String value) 427 { 428 selenium.addCustomRequestHeader(key, value); 429 } 430 431 @Override 432 public void addLocationStrategy(String strategyName, String functionDefinition) 433 { 434 selenium.addLocationStrategy(strategyName, functionDefinition); 435 } 436 437 @Override 438 public void addScript(String scriptContent, String scriptTagId) 439 { 440 selenium.addScript(scriptContent, scriptTagId); 441 } 442 443 @Override 444 public void addSelection(String locator, String optionLocator) 445 { 446 selenium.addSelection(locator, optionLocator); 447 } 448 449 @Override 450 public void allowNativeXpath(String allow) 451 { 452 selenium.allowNativeXpath(allow); 453 } 454 455 @Override 456 public void altKeyDown() 457 { 458 selenium.altKeyDown(); 459 } 460 461 @Override 462 public void altKeyUp() 463 { 464 selenium.altKeyUp(); 465 } 466 467 @Override 468 public void answerOnNextPrompt(String answer) 469 { 470 selenium.answerOnNextPrompt(answer); 471 } 472 473 @Override 474 public void assignId(String locator, String identifier) 475 { 476 selenium.assignId(locator, identifier); 477 } 478 479 @Override 480 public void attachFile(String fieldLocator, String fileLocator) 481 { 482 selenium.attachFile(fieldLocator, fileLocator); 483 } 484 485 @Override 486 public void captureEntirePageScreenshot(String filename, String kwargs) 487 { 488 selenium.captureEntirePageScreenshot(filename, kwargs); 489 } 490 491 @Override 492 public String captureEntirePageScreenshotToString(String kwargs) 493 { 494 return selenium.captureEntirePageScreenshotToString(kwargs); 495 } 496 497 @Override 498 public String captureNetworkTraffic(String type) 499 { 500 return selenium.captureNetworkTraffic(type); 501 } 502 503 @Override 504 public void captureScreenshot(String filename) 505 { 506 selenium.captureScreenshot(filename); 507 } 508 509 @Override 510 public String captureScreenshotToString() 511 { 512 return selenium.captureScreenshotToString(); 513 } 514 515 @Override 516 public void check(String locator) 517 { 518 selenium.check(locator); 519 } 520 521 @Override 522 public void chooseCancelOnNextConfirmation() 523 { 524 selenium.chooseCancelOnNextConfirmation(); 525 } 526 527 @Override 528 public void chooseOkOnNextConfirmation() 529 { 530 selenium.chooseOkOnNextConfirmation(); 531 } 532 533 @Override 534 public void click(String locator) 535 { 536 selenium.click(locator); 537 } 538 539 @Override 540 public void clickAt(String locator, String coordString) 541 { 542 selenium.clickAt(locator, coordString); 543 } 544 545 @Override 546 public void close() 547 { 548 selenium.close(); 549 } 550 551 @Override 552 public void contextMenu(String locator) 553 { 554 selenium.contextMenu(locator); 555 } 556 557 @Override 558 public void contextMenuAt(String locator, String coordString) 559 { 560 selenium.contextMenuAt(locator, coordString); 561 } 562 563 @Override 564 public void controlKeyDown() 565 { 566 selenium.controlKeyDown(); 567 } 568 569 @Override 570 public void controlKeyUp() 571 { 572 selenium.controlKeyUp(); 573 } 574 575 @Override 576 public void createCookie(String nameValuePair, String optionsString) 577 { 578 selenium.createCookie(nameValuePair, optionsString); 579 } 580 581 @Override 582 public void deleteAllVisibleCookies() 583 { 584 selenium.deleteAllVisibleCookies(); 585 } 586 587 @Override 588 public void deleteCookie(String name, String optionsString) 589 { 590 selenium.deleteCookie(name, optionsString); 591 } 592 593 @Override 594 public void deselectPopUp() 595 { 596 selenium.deselectPopUp(); 597 } 598 599 @Override 600 public void doubleClick(String locator) 601 { 602 selenium.doubleClick(locator); 603 } 604 605 @Override 606 public void doubleClickAt(String locator, String coordString) 607 { 608 selenium.doubleClickAt(locator, coordString); 609 } 610 611 @Override 612 public void dragAndDrop(String locator, String movementsString) 613 { 614 selenium.dragAndDrop(locator, movementsString); 615 } 616 617 @Override 618 public void dragAndDropToObject(String locatorOfObjectToBeDragged, String locatorOfDragDestinationObject) 619 { 620 selenium.dragAndDropToObject(locatorOfObjectToBeDragged, locatorOfDragDestinationObject); 621 } 622 623 @Override 624 public void dragdrop(String locator, String movementsString) 625 { 626 selenium.dragdrop(locator, movementsString); 627 } 628 629 @Override 630 public void fireEvent(String locator, String eventName) 631 { 632 selenium.fireEvent(locator, eventName); 633 } 634 635 @Override 636 public void focus(String locator) 637 { 638 selenium.focus(locator); 639 } 640 641 @Override 642 public String getAlert() 643 { 644 return selenium.getAlert(); 645 } 646 647 @Override 648 public String[] getAllButtons() 649 { 650 return selenium.getAllButtons(); 651 } 652 653 @Override 654 public String[] getAllFields() 655 { 656 return selenium.getAllFields(); 657 } 658 659 @Override 660 public String[] getAllLinks() 661 { 662 return selenium.getAllLinks(); 663 } 664 665 @Override 666 public String[] getAllWindowIds() 667 { 668 return selenium.getAllWindowIds(); 669 } 670 671 @Override 672 public String[] getAllWindowNames() 673 { 674 return selenium.getAllWindowNames(); 675 } 676 677 @Override 678 public String[] getAllWindowTitles() 679 { 680 return selenium.getAllWindowTitles(); 681 } 682 683 @Override 684 public String getAttribute(String attributeLocator) 685 { 686 return selenium.getAttribute(attributeLocator); 687 } 688 689 @Override 690 public String[] getAttributeFromAllWindows(String attributeName) 691 { 692 return selenium.getAttributeFromAllWindows(attributeName); 693 } 694 695 @Override 696 public String getBodyText() 697 { 698 return selenium.getBodyText(); 699 } 700 701 @Override 702 public String getConfirmation() 703 { 704 return selenium.getConfirmation(); 705 } 706 707 @Override 708 public String getCookie() 709 { 710 return selenium.getCookie(); 711 } 712 713 @Override 714 public String getCookieByName(String name) 715 { 716 return selenium.getCookieByName(name); 717 } 718 719 @Override 720 public Number getCursorPosition(String locator) 721 { 722 return selenium.getCursorPosition(locator); 723 } 724 725 @Override 726 public Number getElementHeight(String locator) 727 { 728 return selenium.getElementHeight(locator); 729 } 730 731 @Override 732 public Number getElementIndex(String locator) 733 { 734 return selenium.getElementIndex(locator); 735 } 736 737 @Override 738 public Number getElementPositionLeft(String locator) 739 { 740 return selenium.getElementPositionLeft(locator); 741 } 742 743 @Override 744 public Number getElementPositionTop(String locator) 745 { 746 return selenium.getElementPositionTop(locator); 747 } 748 749 @Override 750 public Number getElementWidth(String locator) 751 { 752 return selenium.getElementWidth(locator); 753 } 754 755 @Override 756 public String getEval(String script) 757 { 758 return selenium.getEval(script); 759 } 760 761 @Override 762 public String getExpression(String expression) 763 { 764 return selenium.getExpression(expression); 765 } 766 767 @Override 768 public String getHtmlSource() 769 { 770 return selenium.getHtmlSource(); 771 } 772 773 @Override 774 public String getLocation() 775 { 776 return selenium.getLocation(); 777 } 778 779 @Override 780 public String getLog() 781 { 782 return selenium.getLog(); 783 } 784 785 @Override 786 public Number getMouseSpeed() 787 { 788 return selenium.getMouseSpeed(); 789 } 790 791 @Override 792 public String getPrompt() 793 { 794 return selenium.getPrompt(); 795 } 796 797 @Override 798 public String getSelectedId(String selectLocator) 799 { 800 return selenium.getSelectedId(selectLocator); 801 } 802 803 @Override 804 public String[] getSelectedIds(String selectLocator) 805 { 806 return selenium.getSelectedIds(selectLocator); 807 } 808 809 @Override 810 public String getSelectedIndex(String selectLocator) 811 { 812 return selenium.getSelectedIndex(selectLocator); 813 } 814 815 @Override 816 public String[] getSelectedIndexes(String selectLocator) 817 { 818 return selenium.getSelectedIndexes(selectLocator); 819 } 820 821 @Override 822 public String getSelectedLabel(String selectLocator) 823 { 824 return selenium.getSelectedLabel(selectLocator); 825 } 826 827 @Override 828 public String[] getSelectedLabels(String selectLocator) 829 { 830 return selenium.getSelectedLabels(selectLocator); 831 } 832 833 @Override 834 public String getSelectedValue(String selectLocator) 835 { 836 return selenium.getSelectedValue(selectLocator); 837 } 838 839 @Override 840 public String[] getSelectedValues(String selectLocator) 841 { 842 return selenium.getSelectedValues(selectLocator); 843 } 844 845 @Override 846 public String[] getSelectOptions(String selectLocator) 847 { 848 return selenium.getSelectOptions(selectLocator); 849 } 850 851 @Override 852 public String getSpeed() 853 { 854 return selenium.getSpeed(); 855 } 856 857 @Override 858 public String getTable(String tableCellAddress) 859 { 860 return selenium.getTable(tableCellAddress); 861 } 862 863 @Override 864 public String getText(String locator) 865 { 866 return selenium.getText(locator); 867 } 868 869 @Override 870 public String getTitle() 871 { 872 return selenium.getTitle(); 873 } 874 875 @Override 876 public String getValue(String locator) 877 { 878 return selenium.getValue(locator); 879 } 880 881 @Override 882 public boolean getWhetherThisFrameMatchFrameExpression(String currentFrameString, String target) 883 { 884 return selenium.getWhetherThisFrameMatchFrameExpression(currentFrameString, target); 885 } 886 887 @Override 888 public boolean getWhetherThisWindowMatchWindowExpression(String currentWindowString, String target) 889 { 890 return selenium.getWhetherThisWindowMatchWindowExpression(currentWindowString, target); 891 } 892 893 @Override 894 public Number getXpathCount(String xpath) 895 { 896 return selenium.getXpathCount(xpath); 897 } 898 899 @Override 900 public void goBack() 901 { 902 selenium.goBack(); 903 } 904 905 @Override 906 public void highlight(String locator) 907 { 908 selenium.highlight(locator); 909 } 910 911 @Override 912 public void ignoreAttributesWithoutValue(String ignore) 913 { 914 selenium.ignoreAttributesWithoutValue(ignore); 915 } 916 917 @Override 918 public boolean isAlertPresent() 919 { 920 return selenium.isAlertPresent(); 921 } 922 923 @Override 924 public boolean isChecked(String locator) 925 { 926 return selenium.isChecked(locator); 927 } 928 929 @Override 930 public boolean isConfirmationPresent() 931 { 932 return selenium.isConfirmationPresent(); 933 } 934 935 @Override 936 public boolean isCookiePresent(String name) 937 { 938 return selenium.isCookiePresent(name); 939 } 940 941 @Override 942 public boolean isEditable(String locator) 943 { 944 return selenium.isEditable(locator); 945 } 946 947 @Override 948 public boolean isElementPresent(String locator) 949 { 950 return selenium.isElementPresent(locator); 951 } 952 953 @Override 954 public boolean isOrdered(String locator1, String locator2) 955 { 956 return selenium.isOrdered(locator1, locator2); 957 } 958 959 @Override 960 public boolean isPromptPresent() 961 { 962 return selenium.isPromptPresent(); 963 } 964 965 @Override 966 public boolean isSomethingSelected(String selectLocator) 967 { 968 return selenium.isSomethingSelected(selectLocator); 969 } 970 971 @Override 972 public boolean isTextPresent(String pattern) 973 { 974 return selenium.isTextPresent(pattern); 975 } 976 977 @Override 978 public boolean isVisible(String locator) 979 { 980 return selenium.isVisible(locator); 981 } 982 983 @Override 984 public void keyDown(String locator, String keySequence) 985 { 986 selenium.keyDown(locator, keySequence); 987 } 988 989 @Override 990 public void keyDownNative(String keycode) 991 { 992 selenium.keyDownNative(keycode); 993 } 994 995 @Override 996 public void keyPress(String locator, String keySequence) 997 { 998 selenium.keyPress(locator, keySequence); 999 } 1000 1001 @Override 1002 public void keyPressNative(String keycode) 1003 { 1004 selenium.keyPressNative(keycode); 1005 } 1006 1007 @Override 1008 public void keyUp(String locator, String keySequence) 1009 { 1010 selenium.keyUp(locator, keySequence); 1011 } 1012 1013 @Override 1014 public void keyUpNative(String keycode) 1015 { 1016 selenium.keyUpNative(keycode); 1017 } 1018 1019 @Override 1020 public void metaKeyDown() 1021 { 1022 selenium.metaKeyDown(); 1023 } 1024 1025 @Override 1026 public void metaKeyUp() 1027 { 1028 selenium.metaKeyUp(); 1029 } 1030 1031 @Override 1032 public void mouseDown(String locator) 1033 { 1034 selenium.mouseDown(locator); 1035 } 1036 1037 @Override 1038 public void mouseDownAt(String locator, String coordString) 1039 { 1040 selenium.mouseDownAt(locator, coordString); 1041 } 1042 1043 @Override 1044 public void mouseDownRight(String locator) 1045 { 1046 selenium.mouseDownRight(locator); 1047 } 1048 1049 @Override 1050 public void mouseDownRightAt(String locator, String coordString) 1051 { 1052 selenium.mouseDownRightAt(locator, coordString); 1053 } 1054 1055 @Override 1056 public void mouseMove(String locator) 1057 { 1058 selenium.mouseMove(locator); 1059 } 1060 1061 @Override 1062 public void mouseMoveAt(String locator, String coordString) 1063 { 1064 selenium.mouseMoveAt(locator, coordString); 1065 } 1066 1067 @Override 1068 public void mouseOut(String locator) 1069 { 1070 selenium.mouseOut(locator); 1071 } 1072 1073 @Override 1074 public void mouseOver(String locator) 1075 { 1076 selenium.mouseOver(locator); 1077 } 1078 1079 @Override 1080 public void mouseUp(String locator) 1081 { 1082 selenium.mouseUp(locator); 1083 } 1084 1085 @Override 1086 public void mouseUpAt(String locator, String coordString) 1087 { 1088 selenium.mouseUpAt(locator, coordString); 1089 } 1090 1091 @Override 1092 public void mouseUpRight(String locator) 1093 { 1094 selenium.mouseUpRight(locator); 1095 } 1096 1097 @Override 1098 public void mouseUpRightAt(String locator, String coordString) 1099 { 1100 selenium.mouseUpRightAt(locator, coordString); 1101 } 1102 1103 @Override 1104 public void open(String url) 1105 { 1106 selenium.open(url); 1107 } 1108 1109 @Override 1110 public void open(String url, String ignoreResponseCode) 1111 { 1112 selenium.open(url, ignoreResponseCode); 1113 } 1114 1115 @Override 1116 public void openWindow(String url, String windowID) 1117 { 1118 selenium.openWindow(url, windowID); 1119 } 1120 1121 @Override 1122 public void refresh() 1123 { 1124 selenium.refresh(); 1125 } 1126 1127 @Override 1128 public void removeAllSelections(String locator) 1129 { 1130 selenium.removeAllSelections(locator); 1131 } 1132 1133 @Override 1134 public void removeScript(String scriptTagId) 1135 { 1136 selenium.removeScript(scriptTagId); 1137 } 1138 1139 @Override 1140 public void removeSelection(String locator, String optionLocator) 1141 { 1142 selenium.removeSelection(locator, optionLocator); 1143 } 1144 1145 @Override 1146 public String retrieveLastRemoteControlLogs() 1147 { 1148 return selenium.retrieveLastRemoteControlLogs(); 1149 } 1150 1151 @Override 1152 public void rollup(String rollupName, String kwargs) 1153 { 1154 selenium.rollup(rollupName, kwargs); 1155 } 1156 1157 @Override 1158 public void runScript(String script) 1159 { 1160 selenium.runScript(script); 1161 } 1162 1163 @Override 1164 public void select(String selectLocator, String optionLocator) 1165 { 1166 selenium.select(selectLocator, optionLocator); 1167 } 1168 1169 @Override 1170 public void selectFrame(String locator) 1171 { 1172 selenium.selectFrame(locator); 1173 } 1174 1175 @Override 1176 public void selectPopUp(String windowID) 1177 { 1178 selenium.selectPopUp(windowID); 1179 } 1180 1181 @Override 1182 public void selectWindow(String windowID) 1183 { 1184 selenium.selectWindow(windowID); 1185 } 1186 1187 @Override 1188 public void setBrowserLogLevel(String logLevel) 1189 { 1190 selenium.setBrowserLogLevel(logLevel); 1191 } 1192 1193 @Override 1194 public void setContext(String context) 1195 { 1196 selenium.setContext(context); 1197 } 1198 1199 @Override 1200 public void setCursorPosition(String locator, String position) 1201 { 1202 selenium.setCursorPosition(locator, position); 1203 } 1204 1205 @Override 1206 public void setExtensionJs(String extensionJs) 1207 { 1208 selenium.setExtensionJs(extensionJs); 1209 } 1210 1211 @Override 1212 public void setMouseSpeed(String pixels) 1213 { 1214 selenium.setMouseSpeed(pixels); 1215 } 1216 1217 @Override 1218 public void setSpeed(String value) 1219 { 1220 selenium.setSpeed(value); 1221 } 1222 1223 @Override 1224 public void setTimeout(String timeout) 1225 { 1226 selenium.setTimeout(timeout); 1227 } 1228 1229 @Override 1230 public void shiftKeyDown() 1231 { 1232 selenium.shiftKeyDown(); 1233 } 1234 1235 @Override 1236 public void shiftKeyUp() 1237 { 1238 selenium.shiftKeyUp(); 1239 } 1240 1241 @Override 1242 public void showContextualBanner() 1243 { 1244 selenium.showContextualBanner(); 1245 } 1246 1247 @Override 1248 public void showContextualBanner(String className, String methodName) 1249 { 1250 selenium.showContextualBanner(className, methodName); 1251 } 1252 1253 @Override 1254 public void shutDownSeleniumServer() 1255 { 1256 selenium.shutDownSeleniumServer(); 1257 } 1258 1259 @Override 1260 public void start() 1261 { 1262 selenium.start(); 1263 } 1264 1265 @Override 1266 public void start(Object optionsObject) 1267 { 1268 selenium.start(optionsObject); 1269 } 1270 1271 @Override 1272 public void start(String optionsString) 1273 { 1274 selenium.start(optionsString); 1275 } 1276 1277 @Override 1278 public void stop() 1279 { 1280 selenium.stop(); 1281 } 1282 1283 @Override 1284 public void submit(String formLocator) 1285 { 1286 selenium.submit(formLocator); 1287 } 1288 1289 @Override 1290 public void type(String locator, String value) 1291 { 1292 selenium.type(locator, value); 1293 } 1294 1295 @Override 1296 public void typeKeys(String locator, String value) 1297 { 1298 selenium.typeKeys(locator, value); 1299 } 1300 1301 @Override 1302 public void uncheck(String locator) 1303 { 1304 selenium.uncheck(locator); 1305 } 1306 1307 @Override 1308 public void useXpathLibrary(String libraryName) 1309 { 1310 selenium.useXpathLibrary(libraryName); 1311 } 1312 1313 @Override 1314 public void waitForCondition(String script, String timeout) 1315 { 1316 selenium.waitForCondition(script, timeout); 1317 } 1318 1319 @Override 1320 public void waitForFrameToLoad(String frameAddress, String timeout) 1321 { 1322 selenium.waitForFrameToLoad(frameAddress, timeout); 1323 } 1324 1325 /** 1326 * Waits for page to load, then waits for initialization to finish, which is recognized by the {@code data-page-initialized} attribute 1327 * being set to true on the body element. Polls at increasing intervals, for up-to 30 seconds (that's extraordinarily long, but helps sometimes 1328 * when manually debugging a page that doesn't have the floating console enabled).. 1329 */ 1330 @Override 1331 public void waitForPageToLoad(String timeout) 1332 { 1333 selenium.waitForPageToLoad(timeout); 1334 1335 // In a limited number of cases, a "page" is an container error page or raw HTML content 1336 // that does not include the body element and data-page-initialized element. In those cases, 1337 // there will never be page initialization in the Tapestry sense and we return immediately. 1338 1339 if (!isElementPresent("css=body[data-page-initialized]")) 1340 { 1341 return; 1342 } 1343 1344 final long pollingStartTime = System.currentTimeMillis(); 1345 1346 long sleepTime = 20; 1347 1348 while (true) 1349 { 1350 if (isElementPresent("css=body[data-page-initialized='true']")) 1351 { 1352 return; 1353 } 1354 1355 if ((System.currentTimeMillis() - pollingStartTime) > 30000) 1356 { 1357 reportAndThrowAssertionError("Page did not finish initializing after 30 seconds."); 1358 } 1359 1360 sleep(sleepTime); 1361 1362 sleepTime *= 2; 1363 } 1364 } 1365 1366 @Override 1367 public void waitForPopUp(String windowID, String timeout) 1368 { 1369 selenium.waitForPopUp(windowID, timeout); 1370 } 1371 1372 @Override 1373 public void windowFocus() 1374 { 1375 selenium.windowFocus(); 1376 } 1377 1378 @Override 1379 public void windowMaximize() 1380 { 1381 selenium.windowMaximize(); 1382 } 1383 1384 // --------------------------------------------------------------------- 1385 // End of delegate methods 1386 // --------------------------------------------------------------------- 1387 1388 /** 1389 * Formats a message from the provided arguments, which is written to System.err. In addition, 1390 * captures the AUT's markup, screenshot, and a report to the output directory. 1391 * 1392 * @param message 1393 * @param arguments 1394 * @since 5.4 1395 */ 1396 protected final void reportAndThrowAssertionError(String message, Object... arguments) 1397 { 1398 StringBuilder builder = new StringBuilder(5000); 1399 1400 String formatted = String.format(message, arguments); 1401 1402 builder.append(formatted); 1403 1404 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 1405 1406 StringBuilder buffer = new StringBuilder(5000); 1407 1408 boolean enabled = false; 1409 1410 for (StackTraceElement e : stackTrace) 1411 { 1412 if (enabled) 1413 { 1414 buffer.append("\n- "); 1415 buffer.append(e); 1416 continue; 1417 } 1418 1419 if (e.getMethodName().equals("reportAndThrowAssertionError")) 1420 { 1421 enabled = true; 1422 } 1423 } 1424 1425 writeErrorReport(builder.toString()); 1426 1427 throw new AssertionError(formatted); 1428 } 1429 1430 protected final void unreachable() 1431 { 1432 reportAndThrowAssertionError("An unreachable statement was reached."); 1433 } 1434 1435 /** 1436 * Open the {@linkplain #getBaseURL()}, and waits for the page to load. 1437 */ 1438 protected final void openBaseURL() 1439 { 1440 open(baseURL); 1441 1442 waitForPageToLoad(); 1443 } 1444 1445 /** 1446 * Asserts the text of an element, identified by the locator. 1447 * 1448 * @param locator 1449 * identifies the element whose text value is to be asserted 1450 * @param expected 1451 * expected value for the element's text 1452 */ 1453 protected final void assertText(String locator, String expected) 1454 { 1455 String actual = null; 1456 1457 try 1458 { 1459 actual = getText(locator); 1460 } catch (RuntimeException ex) 1461 { 1462 System.err.printf("Error accessing %s: %s, in:\n\n%s\n\n", locator, ex.getMessage(), getHtmlSource()); 1463 1464 throw ex; 1465 } 1466 1467 if (actual.equals(expected)) 1468 { 1469 return; 1470 } 1471 1472 reportAndThrowAssertionError("%s was '%s' not '%s'", locator, actual, expected); 1473 } 1474 1475 protected final void assertTextPresent(String... text) 1476 { 1477 for (String item : text) 1478 { 1479 if (isTextPresent(item)) 1480 { 1481 continue; 1482 } 1483 1484 reportAndThrowAssertionError("Page did not contain '" + item + "'."); 1485 } 1486 } 1487 1488 /** 1489 * Assets that each string provided is present somewhere in the current document. 1490 * 1491 * @param expected 1492 * string expected to be present 1493 */ 1494 protected final void assertSourcePresent(String... expected) 1495 { 1496 String source = getHtmlSource(); 1497 1498 for (String snippet : expected) 1499 { 1500 if (source.contains(snippet)) 1501 { 1502 continue; 1503 } 1504 1505 reportAndThrowAssertionError("Page did not contain source '" + snippet + "'."); 1506 } 1507 } 1508 1509 /** 1510 * Click a link identified by a locator, then wait for the resulting page to load. 1511 * This is not useful for Ajax updates, just normal full-page refreshes. 1512 * 1513 * @param locator 1514 * identifies the link to click 1515 */ 1516 protected final void clickAndWait(String locator) 1517 { 1518 click(locator); 1519 1520 waitForPageToLoad(); 1521 } 1522 1523 /** 1524 * Waits for the page to load (up to 15 seconds). This is invoked after clicking on an element 1525 * that forces a full page refresh. 1526 */ 1527 protected final void waitForPageToLoad() 1528 { 1529 waitForPageToLoad(PAGE_LOAD_TIMEOUT); 1530 } 1531 1532 /** 1533 * Used when the locator identifies an attribute, not an element. 1534 * 1535 * @param locator 1536 * identifies the attribute whose value is to be asserted 1537 * @param expected 1538 * expected value for the attribute 1539 */ 1540 protected final void assertAttribute(String locator, String expected) 1541 { 1542 String actual = null; 1543 1544 try 1545 { 1546 actual = getAttribute(locator); 1547 } catch (RuntimeException ex) 1548 { 1549 1550 reportAndThrowAssertionError("Error accessing %s: %s", locator, ex.getMessage()); 1551 } 1552 1553 if (actual.equals(expected)) 1554 { 1555 return; 1556 } 1557 1558 reportAndThrowAssertionError("%s was '%s' not '%s'", locator, actual, expected); 1559 } 1560 1561 /** 1562 * Assets that the value in the field matches the expectation 1563 * 1564 * @param locator 1565 * identifies the field 1566 * @param expected 1567 * expected value for the field 1568 * @since 5.3 1569 */ 1570 protected final void assertFieldValue(String locator, String expected) 1571 { 1572 try 1573 { 1574 assertEquals(getValue(locator), expected); 1575 } catch (AssertionError ex) 1576 { 1577 reportAndThrowAssertionError("Failure accessing %s: %s", locator, ex); 1578 } 1579 } 1580 1581 /** 1582 * Opens the base URL, then clicks through a series of links to get to a desired application 1583 * state. 1584 * 1585 * @since 5.3 1586 */ 1587 protected final void openLinks(String... linkText) 1588 { 1589 openBaseURL(); 1590 1591 for (String text : linkText) 1592 { 1593 clickAndWait("link=" + text); 1594 } 1595 } 1596 1597 /** 1598 * Sleeps for the indicated number of seconds. 1599 * 1600 * @since 5.3 1601 */ 1602 protected final void sleep(long millis) 1603 { 1604 try 1605 { 1606 Thread.sleep(millis); 1607 } catch (InterruptedException ex) 1608 { 1609 // Ignore. 1610 } 1611 } 1612 1613 /** 1614 * Waits, up to the page load limit for an element (identified by a CSS rule) to exist 1615 * (it is not assured that the element will be visible). 1616 * 1617 * This implementation only works if the application provides a function onto the 1618 * window object: "testSupport.findCSSMatchCount()" which accepts a CSS rule and returns the number 1619 * of matching elements. 1620 * 1621 * @param cssSelector 1622 * used to locate the element 1623 * @since 5.3 1624 * @deprecated Deprecated in 5.4 with no replacement 1625 */ 1626 protected void waitForCSSSelectedElementToAppear(String cssSelector) 1627 { 1628 String condition = String.format("window.testSupport.findCSSMatchCount(\"%s\") > 0", cssSelector); 1629 1630 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1631 } 1632 1633 /** 1634 * Waits for the element with the given client-side id to be present in the DOM ( 1635 * does not assure that the element is visible). 1636 * 1637 * @param elementId 1638 * identifies the element 1639 * @since 5.3 1640 */ 1641 protected final void waitForElementToAppear(String elementId) 1642 { 1643 1644 String condition = String.format("selenium.browserbot.getCurrentWindow().document.getElementById(\"%s\")", elementId); 1645 1646 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1647 } 1648 1649 /** 1650 * Waits for the element to be removed from the DOM. 1651 * 1652 * 1653 * This implementation depends on window being extended with testSupport.isNotVisible(). 1654 * 1655 * @param elementId 1656 * client-side id of element 1657 * @since 5.3 1658 * @deprecated Deprecated in 5.4 with no replacement 1659 */ 1660 protected final void waitForElementToDisappear(String elementId) 1661 { 1662 String condition = String.format("selenium.browserbot.getCurrentWindow().testSupport.doesNotExist(\"%s\")", elementId); 1663 1664 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1665 } 1666 1667 /** 1668 * Waits for the element specified by the selector to become visible 1669 * Note that waitForElementToAppear waits for the element to be present in the dom, visible or not. waitForVisible 1670 * waits for an element that already exists in the dom to become visible. 1671 * 1672 * @param selector 1673 * element selector 1674 * @since 5.3 1675 */ 1676 protected final void waitForVisible(String selector) 1677 { 1678 String condition = String.format("selenium.isVisible(\"%s\")", selector); 1679 1680 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1681 } 1682 1683 /** 1684 * Waits for the element specified by the selector to become invisible 1685 * Note that waitForElementToDisappear waits for the element to be absent from the dom, visible or not. waitForInvisible 1686 * waits for an existing element to become invisible. 1687 * 1688 * @param selector 1689 * element selector 1690 * @since 5.3 1691 */ 1692 protected final void waitForInvisible(String selector) 1693 { 1694 String condition = String.format("!selenium.isVisible(\"%s\")", selector); 1695 1696 waitForCondition(condition, PAGE_LOAD_TIMEOUT); 1697 } 1698 1699 /** 1700 * Asserts that the current page's title matches the expected value. 1701 * 1702 * @param expected 1703 * value for title 1704 * @since 5.3 1705 */ 1706 protected final void assertTitle(String expected) 1707 { 1708 try 1709 { 1710 assertEquals(getTitle(), expected); 1711 } catch (AssertionError ex) 1712 { 1713 reportAndThrowAssertionError("Unexpected title: %s", ex); 1714 1715 throw ex; 1716 } 1717 } 1718 1719 /** 1720 * Waits until all active XHR requests are completed. 1721 * 1722 * @param timeout 1723 * timeout to wait for (no longer used) 1724 * @since 5.3 1725 * @deprecated Deprecated in 5.4 in favor of the version without a timeout 1726 */ 1727 protected final void waitForAjaxRequestsToComplete(String timeout) 1728 { 1729 waitForAjaxRequestsToComplete(); 1730 } 1731 1732 1733 /** 1734 * Waits until all active XHR requests (as noted by the t5/core/dom module) 1735 * have completed. 1736 * 1737 * @since 5.4 1738 */ 1739 protected final void waitForAjaxRequestsToComplete() 1740 { 1741 // Ugly but necessary. Give the Ajax operation sufficient time to execute normally, then start 1742 // polling to see if it has complete. 1743 sleep(250); 1744 1745 // The t5/core/dom module tracks how many Ajax requests are active 1746 // and body[data-ajax-active] as appropriate. 1747 1748 for (int i = 0; i < 10; i++) 1749 { 1750 if (i > 0) 1751 { 1752 sleep(100); 1753 } 1754 1755 if (getCssCount("body[data-ajax-active='0']").equals(1)) 1756 { 1757 return; 1758 } 1759 } 1760 1761 reportAndThrowAssertionError("Body 'data-ajax-active' attribute never reverted to '0'."); 1762 } 1763 1764 @Override 1765 public Number getCssCount(String str) 1766 { 1767 return selenium.getCssCount(str); 1768 } 1769 1770}