001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.testtools.selenium; 017 018import com.thoughtworks.selenium.SeleneseTestBase; 019import org.apache.commons.lang.StringUtils; 020import org.apache.commons.lang3.exception.ExceptionUtils; 021import org.openqa.selenium.Alert; 022import org.openqa.selenium.By; 023import org.openqa.selenium.JavascriptExecutor; 024import org.openqa.selenium.NoSuchFrameException; 025import org.openqa.selenium.Proxy; 026import org.openqa.selenium.WebDriver; 027import org.openqa.selenium.WebElement; 028import org.openqa.selenium.chrome.ChromeDriver; 029import org.openqa.selenium.chrome.ChromeDriverService; 030import org.openqa.selenium.firefox.FirefoxDriver; 031import org.openqa.selenium.firefox.FirefoxProfile; 032import org.openqa.selenium.remote.CapabilityType; 033import org.openqa.selenium.remote.DesiredCapabilities; 034import org.openqa.selenium.remote.RemoteWebDriver; 035import org.openqa.selenium.safari.SafariDriver; 036 037import java.io.BufferedReader; 038import java.io.File; 039import java.io.InputStream; 040import java.io.InputStreamReader; 041import java.net.MalformedURLException; 042import java.net.URL; 043import java.text.SimpleDateFormat; 044import java.util.Calendar; 045import java.util.Date; 046import java.util.LinkedList; 047import java.util.List; 048import java.util.concurrent.TimeUnit; 049 050/** 051 * <p> 052 * The goal of the WebDriverUtils class is to invert the dependencies on WebDriver from {@see WebDriverLegacyITBase} for reuse 053 * without having to extend WebDriverLegacyITBase. 054 * </p><p> 055 * For compatibility with {@see JiraAwareFailureUtils}, external test framework asserts and fails should not be called from 056 * WebDriverUtils, instead use {@see JiraAwareAftBase}. 057 * </p><p> 058 * For the first example see waitFor 059 * </p> 060 * @see WebDriverLegacyITBase 061 * @author Kuali Rice Team (rice.collab@kuali.org) 062 */ 063public class WebDriverUtils { 064 065 protected static SauceLabsWebDriverHelper saucelabs; 066 067 protected static WebDriverHighlightHelper webDriverHighlightHelper; 068 069 public static boolean jGrowlEnabled = false; 070 071 /** 072 * http://localhost:8080/kr-dev 073 */ 074 public static final String DEFAULT_BASE_URL = "http://localhost:8080/kr-dev"; 075 076 /** 077 * http://localhost:8080/krad-dev 078 */ 079 public static final String DEFAULT_BASE_URL_KRAD = "http://localhost:8080/krad-dev"; 080 081 /** 082 * <p> 083 * Set to true to not close the browser after the test has run. 084 * </p><p> 085 * -Dremote.driver.dontTearDown=true 086 * </p> 087 */ 088 public static final String DONT_TEAR_DOWN_PROPERTY = "remote.driver.dontTearDown"; 089 090 /** 091 * <p> 092 * Set to true to not close the browser after the test has if the test failed. 093 * </p><p> 094 * -Dremote.driver.dontTearDownOnFailure=true 095 * </p> 096 */ 097 public static final String DONT_TEAR_DOWN_ON_FAILURE_PROPERTY = "remote.driver.dontTearDownOnFailure"; 098 099 /** 100 * remote.public.driver 101 */ 102 public static final String HUB_DRIVER_PROPERTY = "remote.public.driver"; 103 104 /** 105 * For use when running Selenium tests through a Selenium Hub. 106 * -Dremote.public.hub= 107 */ 108 public static final String HUB_PROPERTY = "remote.public.hub"; 109 110 /** 111 * http://localhost:4444/wd/hub 112 */ 113 public static final String HUB_URL_PROPERTY = "http://localhost:4444/wd/hub"; 114 115 /** 116 * wait Methods inter loop sleep period, default of 1000 Milliseconds. 117 * TODO parametrize for JVM Arg 118 */ 119 public static int IMPLICIT_WAIT_TIME_LOOP_MS = 1000; 120 121 /** 122 * <p> 123 * {@see IMPLICIT_WAIT_TIME_SECONDS_DEFAULT} to configure, default 30 seconds. 124 * </p><p> 125 * In code don't use this variable but call {@see configuredImplicityWait} to get the configured value. 126 * </p> 127 */ 128 public static int IMPLICIT_WAIT_TIME_SECONDS_DEFAULT = 30; 129 130 /** 131 * If true tests will fail on jGrowl errors, default of false 132 * TODO upgrade to config via JVM param. 133 */ 134 public static final boolean JGROWL_ERROR_FAILURE = false; 135 136 /** 137 * <p> 138 * Local proxy used for running tests thru jmeter. 139 * </p><p> 140 * Include host name and port number. Example: localhost:7777 141 * </p><p> 142 * -Dremote.public.proxy= 143 * </p> 144 */ 145 public static final String PROXY_HOST_PROPERTY = "remote.public.proxy"; 146 147 /** 148 * <p> 149 * Skip automatice login if set to anything other than true/ 150 * </p><p> 151 * -Dremote.autologin=false 152 * </p> 153 */ 154 public static final String REMOTE_AUTOLOGIN_PROPERTY = "remote.autologin"; 155 156 /** 157 * <p> 158 * Set to true to enable jGrowl test messages. 159 * </p><p> 160 * When enabled, jGrowl messages will be sent when clicking on buttons and links identified by their text. 161 * </p><p> 162 * -Dremote.jgrowl.enabled=true 163 * </p> 164 */ 165 public static final String REMOTE_JGROWL_ENABLED = "remote.jgrowl.enabled"; 166 167 /** 168 * Set -Dremote.login.uif=KNS to use old login screen. Default value = KRAD 169 */ 170 public static final String REMOTE_LOGIN_UIF = "remote.login.uif"; 171 172 /** 173 * Set -Dremote.property.file= to load proprties from file 174 */ 175 public static final String REMOTE_PROPERTIES_PROPERTY = "remote.property.file"; 176 177 /** 178 * Set -Dremote.public.chrome= or WEBDRIVER_CHROME_DRIVER 179 */ 180 public static final String REMOTE_PUBLIC_CHROME = "remote.public.chrome"; 181 182 /** 183 * -Dremote.public.url= 184 */ 185 public static final String REMOTE_PUBLIC_URL_PROPERTY = "remote.public.url"; 186 187 /** 188 * <p> 189 * Set -Dremote.public.wait.seconds to override DEFAULT_WAIT_SEC. 190 * </p><p> 191 * {@see IMPLICIT_WAIT_TIME_SECONDS_DEFAULT} 192 * </p> 193 */ 194 public static final String REMOTE_PUBLIC_WAIT_SECONDS_PROPERTY = "remote.public.wait.seconds"; 195 196 /** 197 * Set -Dremote.public.user= to the username to login as 198 */ 199 public static final String REMOTE_PUBLIC_USER_PROPERTY = "remote.public.user"; 200 201 /** 202 * You probably don't want to really be using a userpool, set -Dremote.public.userpool= to base url if you must. 203 */ 204 public static final String REMOTE_PUBLIC_USERPOOL_PROPERTY = "remote.public.userpool"; 205 206 /** 207 * <p> 208 * Time to wait for the URL used in setup to load, 120 seconds by default. 209 * </p><p> 210 * Sometimes this is the first hit on the app and it needs a bit longer than any other. 211 * </p><p> 212 * TODO parametrize for JVM Arg 213 * <p> 214 */ 215 public static final int SETUP_URL_LOAD_WAIT_SECONDS = 120; 216 217 /** 218 * Selenium's webdriver.chrome.driver parameter, you can set -Dwebdriver.chrome.driver= or Rice's REMOTE_PUBLIC_CHROME. 219 */ 220 public static final String WEBDRIVER_CHROME_DRIVER = "webdriver.chrome.driver"; 221 222 /** 223 * Setup the WebDriver test, login, and load the given web page 224 * 225 * @param className 226 * @param testName 227 * @return driver 228 * @throws Exception 229 */ 230 public static WebDriver setUp(String className, String testName) throws Exception { 231 if ("true".equals(System.getProperty(REMOTE_JGROWL_ENABLED, "false"))) { 232 jGrowlEnabled = true; 233 } 234 235 webDriverHighlightHelper = new WebDriverHighlightHelper(); 236 237 WebDriver driver = null; 238 if (System.getProperty(SauceLabsWebDriverHelper.REMOTE_DRIVER_SAUCELABS_PROPERTY) == null) { 239 driver = getWebDriver(); 240 } else { 241 saucelabs = new SauceLabsWebDriverHelper(); 242 saucelabs.setUp(className, testName); 243 driver = saucelabs.getDriver(); 244 } 245 246 return driver; 247 } 248 249 /** 250 *<p> 251 * Calls {@see SauceLabsWebDriverHelper#tearDown} if {@see #REMOTE_PUBLIC_USERPOOL_PROPERTY} is enabled, calls a user pool 252 * url with the given poolParamTest and poolParamUser. 253 *</p> 254 * 255 * @param passed used by {@see SauceLabsWebDriverHelper#tearDown} to record Saucelabs test status can be null if Saucelabs 256 * is not being used 257 * @param sessionId used by {@see SauceLabsWebDriverHelper#tearDown} to record Saucelabs sessionId status can be null if Saucelabs 258 * is not being used 259 * @param poolParamTest can be null unless a user pool is being used 260 * @param poolParamUser can be null unless a user pool is being used 261 * @throws Exception 262 */ 263 public static void tearDown(boolean passed, String sessionId, String poolParamTest, String poolParamUser, String className, String testName) throws Exception { 264 265 if (passed) { 266 System.out.println("Registering session passed " + sessionId); 267 } else { 268 System.out.println("Registering session failed " + sessionId); 269 } 270 271 if (System.getProperty(SauceLabsWebDriverHelper.REMOTE_DRIVER_SAUCELABS_PROPERTY) != null) { 272 saucelabs.tearDown(passed, sessionId, className, testName); 273 } 274 275 if (System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) { 276 AutomatedFunctionalTestUtils.getHTML(AutomatedFunctionalTestUtils.prettyHttp(System.getProperty( 277 REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + poolParamTest + "&user=" + poolParamUser)); 278 } 279 } 280 281 /** 282 * <p> 283 * If an alert is present accept it print the alert text to System.out 284 * </p> 285 * 286 * @param driver to accept alert on 287 */ 288 public static void acceptAlertIfPresent(WebDriver driver) { 289 if (WebDriverUtils.isAlertPresent(driver)) { 290 System.out.println("Alert present " + WebDriverUtils.alertText(driver)); 291 try { 292 alertAccept(driver); 293 } catch (Exception e) { 294 // Don't fail on alert exception 295 } 296 } 297 } 298 299 /** 300 * <p> 301 * Accept the javascript alert (clicking OK). 302 * </p> 303 * 304 * @param driver WebDriver to accept alert on 305 */ 306 public static void alertAccept(WebDriver driver) { 307 Alert alert = driver.switchTo().alert(); 308 jGrowl(driver, "AFT Step", false, "AFT Step: Accept Alert " + WebDriverUtils.alertText(driver)); 309 alert.accept(); 310 } 311 312 /** 313 * <p> 314 * Dismiss the javascript alert (clicking Cancel). 315 * </p> 316 * 317 * @param driver WebDriver to dismiss alert on 318 */ 319 public static void alertDismiss(WebDriver driver) { 320 Alert alert = driver.switchTo().alert(); 321 jGrowl(driver, "AFT Step", false, "AFT Step: Dismiss Alert " + WebDriverUtils.alertText(driver)); 322 alert.dismiss(); 323 } 324 325 /** 326 * <p> 327 * Return alert text. 328 * </p> 329 * 330 * @param driver to get alert text from 331 * @return alert text 332 */ 333 public static String alertText(WebDriver driver) { 334 return driver.switchTo().alert().getText(); 335 } 336 337 /** 338 * <p> 339 * <a href="http://code.google.com/p/chromedriver/downloads/list">ChromeDriver downloads</a>, {@see #REMOTE_PUBLIC_CHROME}, 340 * {@see #WEBDRIVER_CHROME_DRIVER}, and {@see #HUB_DRIVER_PROPERTY} 341 * </p> 342 * 343 * @return chromeDriverService 344 */ 345 public static ChromeDriverService chromeDriverCreateCheck() { 346 String driverParam = System.getProperty(HUB_DRIVER_PROPERTY); 347 // TODO can the saucelabs driver stuff be leveraged here? 348 if (driverParam != null && "chrome".equals(driverParam.toLowerCase())) { 349 if (System.getProperty(WEBDRIVER_CHROME_DRIVER) == null) { 350 if (System.getProperty(REMOTE_PUBLIC_CHROME) != null) { 351 System.setProperty(WEBDRIVER_CHROME_DRIVER, System.getProperty(REMOTE_PUBLIC_CHROME)); 352 } 353 } 354 try { 355 ChromeDriverService chromeDriverService = new ChromeDriverService.Builder() 356 .usingDriverExecutable(new File(System.getProperty(WEBDRIVER_CHROME_DRIVER))) 357 .usingAnyFreePort() 358 .build(); 359 return chromeDriverService; 360 } catch (Throwable t) { 361 throw new RuntimeException("Exception starting chrome driver service, is chromedriver ( http://code.google.com/p/chromedriver/downloads/list ) installed? You can include the path to it using -Dremote.public.chrome", t) ; 362 } 363 } 364 return null; 365 } 366 367 /** 368 * <p> 369 * Return the configured implicity wait seconds, {@see #REMOTE_PUBLIC_WAIT_SECONDS_PROPERTY} and {@see #IMPLICIT_WAIT_TIME_SECONDS_DEFAULT}. 370 * </p> 371 * 372 * @return seconds for implicity wait 373 */ 374 public static int configuredImplicityWait() { 375 return Integer.parseInt(System.getProperty(REMOTE_PUBLIC_WAIT_SECONDS_PROPERTY, IMPLICIT_WAIT_TIME_SECONDS_DEFAULT + "")); 376 } 377 378 /** 379 * <p> 380 * Remove double line spacing. 381 * </p> 382 * 383 * @param contents String to remove double line spacing from 384 * @return String with double line spacing removed. 385 */ 386 public static String deLinespace(String contents) { 387 while (contents.contains("\n\n")) { 388 contents = contents.replaceAll("\n\n", "\n"); 389 } 390 391 return contents; 392 } 393 394 /** 395 * <p> 396 * If {@see #REMOTE_PUBLIC_USER_PROPERTY} property is set, return its value, else if {@see #REMOTE_PUBLIC_USERPOOL_PROPERTY} 397 * is set use it to query the userpool service passing the testParam. 398 * </p> 399 * * 400 * @param testParam to use if using a user pool 401 * @return user 402 */ 403 public static String determineUser(String testParam) { 404 String user = null; 405 406 if (System.getProperty(REMOTE_PUBLIC_USER_PROPERTY) != null) { 407 return System.getProperty(REMOTE_PUBLIC_USER_PROPERTY); 408 } else if (System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) { // deprecated 409 String userResponse = AutomatedFunctionalTestUtils.getHTML(AutomatedFunctionalTestUtils.prettyHttp( 410 System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + testParam.trim())); 411 return userResponse.substring(userResponse.lastIndexOf(":") + 2, userResponse.lastIndexOf("\"")); 412 } 413 414 return user; 415 } 416 417 /** 418 * <p> 419 * Setting the JVM arg remote.driver.dontTearDown to y or t leaves the browser window open when the test has completed. 420 * <p></p> 421 * Valuable when debugging, updating, or creating new tests. When implementing your own tearDown method rather than an 422 * inherited one, it is a common courtesy to include this check and not stop and shutdown the browser window to make it 423 * easy debug or update your test. 424 * </p> 425 * 426 * @return true if the dontTearDownProperty is not set. 427 */ 428 public static boolean dontTearDownPropertyNotSet() { 429 return System.getProperty(DONT_TEAR_DOWN_PROPERTY) == null || 430 "f".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase()) || 431 "n".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase()); 432 } 433 434 /** 435 * Given the boolean parameter and depending on if {@see #DONT_TEAR_DOWN_ON_FAILURE_PROPERTY} is set to something other 436 * than n, don't tear down the browser window on a test failure. 437 438 * @param passed 439 * @return 440 */ 441 public static boolean dontTearDownOnFailure(boolean passed) { 442 if (!"n".equalsIgnoreCase(System.getProperty(DONT_TEAR_DOWN_ON_FAILURE_PROPERTY, "n"))) { 443 return passed; 444 } 445 return true; 446 } 447 448 /** 449 * <p> 450 * Find Button by text. 451 * </p> 452 * 453 * @param driver to find button on 454 * @param buttonText text to find button by 455 * @return WebElement of button with button text 456 */ 457 public static WebElement findButtonByText(WebDriver driver, String buttonText) { 458 return findElement(driver, By.xpath("//button[contains(text(), '" + buttonText + "')]")); 459 } 460 461 /** 462 * <p> 463 * Find and highlight the WebElement using the given WebDriver and By. 464 * </p> 465 * 466 * @param driver driver to find on 467 * @param by selector to find 468 * @return 469 */ 470 public static WebElement findElement(WebDriver driver, By by) { 471 WebElement found = driver.findElement(by); 472 WebDriverUtils.highlightElement(driver, found); 473 return found; 474 } 475 476 /** 477 * <p> 478 * In order to run as a smoke test the ability to set the baseUrl via the JVM arg remote.public.url is required. 479 * </p><p> 480 * Trailing slashes are trimmed. If the remote.public.url does not start with http:// it will be added. 481 * </p> 482 * 483 * @return http://localhost:8080/kr-dev by default else the value of remote.public.url 484 */ 485 public static String getBaseUrlString() { 486 String baseUrl = System.getProperty(REMOTE_PUBLIC_URL_PROPERTY); 487 if (baseUrl == null) { 488 baseUrl = DEFAULT_BASE_URL; 489 } 490 baseUrl = AutomatedFunctionalTestUtils.prettyHttp(baseUrl); 491 return baseUrl; 492 } 493 494 /** 495 * yyyy-MM-dd-HH-mm-ss 496 * 497 * @return Date formatted as yyyy-MM-dd-HH-mm-ss 498 */ 499 public static String getDateTimeStampFormatted() { 500 Date now = Calendar.getInstance().getTime(); 501 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); 502 return sdf.format(now); 503 } 504 505 /** 506 * <p> 507 * Return the WebElement that has the attribute name with the given value. 508 * </p> 509 * 510 * @param driver to get element from 511 * @param attributeName attribute name to find element by 512 * @param value for the attribute name to find element by 513 * @return WebElement 514 */ 515 public static WebElement getElementByAttributeValue(WebDriver driver, String attributeName, String value){ 516 return findElement(driver, By.cssSelector("[" + attributeName + "='" + value +"']")); 517 } 518 519 /** 520 * <p> 521 * In order to run as a smoke test under selenium grid the ability to set the hubUrl via the JVM arg remote.public.hub is required. 522 * </p><p> 523 * Trailing slashes are trimmed. If the remote.public.hub does not start with http:// it will be added. 524 * </p> 525 * 526 * @return http://localhost:4444/wd/hub by default else the value of remote.public.hub 527 */ 528 public static String getHubUrlString() { 529 String hubUrl = System.getProperty(HUB_PROPERTY); 530 if (hubUrl == null) { 531 hubUrl = HUB_URL_PROPERTY; 532 } 533 hubUrl = AutomatedFunctionalTestUtils.prettyHttp(hubUrl); 534 if (!hubUrl.endsWith("/wd/hub")) { 535 hubUrl = hubUrl + "/wd/hub"; 536 } 537 return hubUrl; 538 } 539 540 /** 541 * <p> 542 * remote.public.driver set to chrome or firefox (null assumes firefox). 543 * </p><p> 544 * if remote.public.hub is set a RemoteWebDriver is created (Selenium Grid) 545 * if proxy.host is set, the web driver is setup to use a proxy 546 * </p> 547 * 548 * @return WebDriver or null if unable to create 549 */ 550 public static WebDriver getWebDriver() { 551 String driverParam = System.getProperty(HUB_DRIVER_PROPERTY); 552 String hubParam = System.getProperty(HUB_PROPERTY); 553 String proxyParam = System.getProperty(PROXY_HOST_PROPERTY); 554 555 // setup proxy if specified as VM Arg 556 DesiredCapabilities capabilities = new DesiredCapabilities(); 557 WebDriver webDriver = null; 558 if (StringUtils.isNotEmpty(proxyParam)) { 559 capabilities.setCapability(CapabilityType.PROXY, new Proxy().setHttpProxy(proxyParam)); 560 } 561 562 if (hubParam == null) { 563 if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) { 564 FirefoxProfile profile = new FirefoxProfile(); 565 profile.setEnableNativeEvents(false); 566 capabilities.setCapability(FirefoxDriver.PROFILE, profile); 567 return new FirefoxDriver(capabilities); 568 } else if ("chrome".equalsIgnoreCase(driverParam)) { 569 return new ChromeDriver(capabilities); 570 } else if ("safari".equals(driverParam)) { 571 System.out.println("SafariDriver probably won't work, if it does please contact Erik M."); 572 return new SafariDriver(capabilities); 573 } 574 } else { 575 try { 576 if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) { 577 return new RemoteWebDriver(new URL(getHubUrlString()), DesiredCapabilities.firefox()); 578 } else if ("chrome".equalsIgnoreCase(driverParam)) { 579 return new RemoteWebDriver(new URL(getHubUrlString()), DesiredCapabilities.chrome()); 580 } 581 } catch (MalformedURLException mue) { 582 System.out.println(getHubUrlString() + " " + mue.getMessage()); 583 mue.printStackTrace(); 584 } 585 } 586 return null; 587 } 588 589 public static void highlightElement(WebDriver webDriver, By by) { 590 List<WebElement> elements = webDriver.findElements(by); 591 for (WebElement element : elements) { 592 WebDriverUtils.highlightElement(webDriver, element); 593 } 594 } 595 596 597 public static void highlightElements(WebDriver webDriver, List<WebElement> webElements) { 598 for (WebElement webElement: webElements) { 599 highlightElement(webDriver, webElement); 600 } 601 } 602 603 /** 604 * <p> 605 * Highlight given WebElement. 606 * </p> 607 * 608 * @param webDriver to execute highlight on 609 * @param webElement to highlight 610 */ 611 public static void highlightElement(WebDriver webDriver, WebElement webElement) { 612 webDriverHighlightHelper.highlightElement(webDriver, webElement); 613 } 614 615 /** 616 * <p> 617 * Return true if an alert is present, false if not. 618 * </p> 619 * 620 * @param driver to check for presents of alert on 621 * @return true if there is an alert present, false if not 622 */ 623 public static boolean isAlertPresent(WebDriver driver) { 624 try { 625 driver.switchTo().alert(); 626 return true; 627 } catch (Exception e) { 628 return false; 629 } 630 } 631 632 public static Boolean isTextPresent(WebDriver driver, String pageText, String text) { 633 boolean textPresent = Boolean.FALSE; 634 if (pageText.contains(text)) { 635 WebDriverUtils.highlightElement(driver, By.xpath("//*[contains(text(), '" + text + "')]")); 636 textPresent = Boolean.TRUE; 637 } 638 WebDriverUtils.jGrowl(driver, "Is Text Present?", false, "Is text '" + text + "' present?" + " " + textPresent); 639 return textPresent; 640 } 641 642 /** 643 * assumes javascript window.ALL_ERRORS contains errors 644 */ 645 public static List javascriptErrors(WebDriver driver) { 646 List errors = new LinkedList(); 647 648 // guard against a NPE below is due to a null WebDriver being passed in 649 if (driver == null) { 650 System.out.println("WebDriver to use to retrieve javascript errors was null!"); 651 return errors; 652 } 653 654 try { 655 String javascript = "return window.ALL_ERRORS;"; 656 List javascriptErrors = (List)((JavascriptExecutor) driver).executeScript(javascript); 657 if (javascriptErrors != null) { 658 errors.addAll(javascriptErrors); 659 } 660 } catch (Throwable t) { 661 // don't fail on problems retrieving javascript errors 662 System.out.println("Problem retrieving javascript errors: " + t.toString()); 663 t.printStackTrace(System.out); 664 } 665 666 return errors; 667 } 668 669 public static String javascriptErrorsToString(List errors) { 670 StringBuilder sb = new StringBuilder(); 671 672 if (errors != null && errors.size() > 0) { 673 for (Object error: errors) { 674 sb.append((String)error).append("\n"); 675 } 676 } 677 678 return sb.toString(); 679 } 680 681 /** 682 * <p> 683 * Display jGrowl. 684 * </p> 685 * 686 * @param driver WebDriver to execute jGrowl on 687 * @param jGrowlHeader header text for jGrowl 688 * @param sticky true to set the jGrowl to sticky, false for not sticky 689 * @param message message to display in the jGrowl 690 * @param throwable message and stacktrace to included in jGrowl 691 */ 692 public static void jGrowl(WebDriver driver, String jGrowlHeader, boolean sticky, String message, Throwable throwable) { 693 if (jGrowlEnabled) { // check if jGrowl is enabled to skip over the stack trace extraction if it is not. 694 jGrowl(driver, jGrowlHeader, sticky, message + " " + throwable.getMessage() + "\n" + ExceptionUtils.getStackTrace(throwable)); 695 } 696 } 697 698 /** 699 * <p> 700 * Display jGrowl. 701 * </p> 702 * 703 * @param driver WebDriver to execute jGrowl on 704 * @param jGrowlHeader header text for jGrowl 705 * @param sticky true to set the jGrowl to sticky, false for not sticky 706 * @param message message to display in the jGrowl 707 */ 708 public static void jGrowl(WebDriver driver, String jGrowlHeader, boolean sticky, String message) { 709 stepMessage(message); 710 message = message.replace("'", "'"); 711 message = message.replace("\"", """); 712 if (jGrowlEnabled) { 713 String javascript="jQuery.jGrowl('" + message + "' , {sticky: " + sticky + ", header : '" + jGrowlHeader + "'});"; 714 try { 715 ((JavascriptExecutor) driver).executeScript(javascript); 716 } catch (Throwable t) { 717 jGrowlException(t, javascript); 718 } 719 } 720 } 721 722 /** 723 * <p> 724 * Print jGrowl Exception to System.out, if {@see #JGROWL_ERROR_FAILURE} is set to true, fail. 725 * </p> 726 * 727 * @param throwable message and stack trace to print and if configured fail with 728 */ 729 public static void jGrowlException(Throwable throwable, String jGrowlJavascript) { 730 String failMessage = jGrowlJavascript + " failed with " + throwable.getMessage() + "\n" 731 + ExceptionUtils.getStackTrace(throwable); 732 System.out.println("jGrowl failure " + failMessage); 733 if (JGROWL_ERROR_FAILURE) { 734 SeleneseTestBase.fail(failMessage); // SeleneseTestBase fail okay here as jGrowl failures are not Jira worthy yet 735 } 736 } 737 738 public static void openTestUrl(WebDriver driver, String testUrl) { 739 driver.manage().timeouts().implicitlyWait(WebDriverUtils.SETUP_URL_LOAD_WAIT_SECONDS, TimeUnit.SECONDS); 740 741 driver.get(testUrl); 742 if (!System.getProperty(SauceLabsWebDriverHelper.SAUCE_BROWSER_PROPERTY,"ff").equals("opera")) { 743 driver.manage().window().maximize(); 744 // driver.manage().window().setSize(new Dimension(800,600)); 745 } 746 747 WebDriverUtils.jGrowl(driver, "Open URL", false, "Open " + testUrl); 748 749 driver.manage().timeouts().implicitlyWait(WebDriverUtils.configuredImplicityWait(), TimeUnit.SECONDS); 750 } 751 752 /** 753 * <p> 754 * Select frame defined by locator without throwing an Exception if it doesn't exist. 755 * </p> 756 * 757 * @param driver to select frame on 758 * @param locator to identify frame to select 759 */ 760 public static void selectFrameSafe(WebDriver driver, String locator) { 761 try { 762 driver.switchTo().frame(locator); 763 } catch (NoSuchFrameException nsfe) { 764 // don't fail 765 } 766 } 767 768 public static void stepMessage(String message) { 769 System.out.println("AFT Step: " + message); 770 } 771 772 /** 773 * <p> 774 * Return the WebElement that has the attribute name with the given value within the given seconds to wait. 775 * </p> 776 * 777 * @param driver to get element from 778 * @param attribute name to find element by 779 * @param attributeValue for the attribute name to find element by 780 * @param waitSeconds number of seconds to wait 781 * @return WebElement 782 * @throws InterruptedException 783 */ 784 public static WebElement waitAndGetElementByAttributeValue(WebDriver driver, String attribute, String attributeValue, int waitSeconds) throws InterruptedException { 785 // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp 786 // driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS); 787 // driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS); 788 789 driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS); 790 791 boolean failed = false; 792 793 for (int second = 0;; second++) { 794 Thread.sleep(1000); 795 if (second >= waitSeconds) { 796 failed = true; 797 } 798 try { 799 if (failed || (getElementByAttributeValue(driver, attribute, attributeValue) != null)) { 800 break; 801 } 802 } catch (Exception e) {} 803 } 804 805 WebElement element = getElementByAttributeValue(driver, attribute, attributeValue); 806 driver.manage().timeouts().implicitlyWait(WebDriverUtils.configuredImplicityWait(), TimeUnit.SECONDS); 807 return element; 808 } 809 810 public static void waitToAcceptAlert(WebDriver driver, int waitSeconds, String message) throws InterruptedException { 811 driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS); 812 813 boolean failed = false; 814 815 for (int second = 0;; second++) { 816 Thread.sleep(1000); 817 if (second >= waitSeconds) { 818 failed = true; 819 } 820 try { 821 if (failed) { 822 break; 823 } else if (isAlertPresent(driver)) { 824 acceptAlertIfPresent(driver); 825 break; 826 } 827 } catch (Exception e) {} 828 } 829 830 driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS); 831 } 832 833 /** 834 * <p> 835 * Wait for the given amount of seconds, for the given by, using the given driver. The message is displayed if the 836 * by cannot be found. No action is performed on the by, so it is possible that the by found is not visible or enabled. 837 * </p> 838 * 839 * @param driver WebDriver to wait on 840 * @param waitSeconds seconds to wait 841 * @param by By to wait for 842 * @param message to display if by is not found in waitSeconds 843 * @throws InterruptedException 844 */ 845 public static WebElement waitFor(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException { 846 // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp 847 // driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS); 848 // driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS); 849 850 driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS); 851 852 boolean failed = false; 853 WebElement element = null; 854 855 for (int second = 0;; second++) { 856 Thread.sleep(1000); 857 if (second >= waitSeconds) { 858 failed = true; 859 } 860 try { 861 if (failed) { 862 break; 863 } else if ((driver.findElements(by)).size() > 0) { 864 element = findElement(driver, by); // NOTICE just the find, no action, so by is found, but might not be visible or enabled. 865 highlightElement(driver, element); 866 break; 867 } 868 } catch (Exception e) {} 869 } 870 871 driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS); 872 return element; 873 } 874 875 /** 876 * <p> 877 * Wait for WebElements. 878 * </p> 879 * 880 * @param driver WebDriver to wait on 881 * @param by By to wait for 882 * @return 883 * @throws InterruptedException 884 */ 885 public static List<WebElement> waitFors(WebDriver driver, By by) throws InterruptedException { 886 return waitFors(driver, configuredImplicityWait(), by, ""); 887 } 888 889 /** 890 * <p> 891 * Wait for WebElements. 892 * </p> 893 * 894 * @param driver WebDriver to wait on 895 * @param by By to wait for 896 * @param message to display if by is not found in waitSeconds 897 * @return List of WebElements found 898 * @throws InterruptedException 899 */ 900 public static List<WebElement> waitFors(WebDriver driver, By by, String message) throws InterruptedException { 901 return waitFors(driver, configuredImplicityWait(), by, message); 902 } 903 904 /** 905 * <p> 906 * Wait for the given amount of seconds, for the given by, using the given driver. The message is displayed if the 907 * by cannot be found. No action is performed on the by, so it is possible that the by found is not visible or enabled. 908 * </p> 909 * 910 * @param driver WebDriver to wait on 911 * @param waitSeconds seconds to wait 912 * @param by By to wait for 913 * @param message to display if by is not found in waitSeconds 914 * @return List of WebElements found 915 * @throws InterruptedException 916 */ 917 public static List<WebElement> waitFors(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException { 918 // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp 919 // driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS); 920 // driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS); 921 922 driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS); 923 924 boolean failed = false; 925 926 for (int second = 0;; second++) { 927 Thread.sleep(1000); 928 if (second >= waitSeconds) { 929 failed = true; 930 } 931 try { 932 if (failed || (driver.findElements(by)).size() > 0) { 933 break; 934 } 935 } catch (Exception e) {} 936 } 937 938 driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS); 939 return driver.findElements(by); // NOTICE just the find, no action, so by is found, but might not be visible or enabled. 940 } 941}