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