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