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