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