001/* 002 * Copyright 2005-2013 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 edu.samplu.common; 017 018import com.thoughtworks.selenium.SeleneseTestBase; 019import org.apache.commons.lang.StringUtils; 020import org.apache.commons.lang3.exception.ExceptionUtils; 021import org.openqa.selenium.By; 022import org.openqa.selenium.JavascriptExecutor; 023import org.openqa.selenium.NoSuchFrameException; 024import org.openqa.selenium.Proxy; 025import org.openqa.selenium.WebDriver; 026import org.openqa.selenium.WebElement; 027import org.openqa.selenium.chrome.ChromeDriver; 028import org.openqa.selenium.chrome.ChromeDriverService; 029import org.openqa.selenium.firefox.FirefoxDriver; 030import org.openqa.selenium.firefox.FirefoxProfile; 031import org.openqa.selenium.remote.CapabilityType; 032import org.openqa.selenium.remote.DesiredCapabilities; 033import org.openqa.selenium.remote.RemoteWebDriver; 034import org.openqa.selenium.safari.SafariDriver; 035 036import java.io.BufferedReader; 037import java.io.File; 038import java.io.InputStream; 039import java.io.InputStreamReader; 040import java.net.MalformedURLException; 041import java.net.URL; 042import java.util.LinkedList; 043import java.util.List; 044import java.util.concurrent.TimeUnit; 045 046/** 047 * The goal of the WebDriverUtil class is to invert the dependencies on WebDriver from WebDriverLegacyITBase for reuse 048 * without having to extend WebDriverLegacyITBase. For the first example see waitFor 049 * 050 * @see WebDriverLegacyITBase 051 * @author Kuali Rice Team (rice.collab@kuali.org) 052 */ 053public class WebDriverUtil { 054 055 public static boolean jGrowlEnabled = false; 056 057 public static boolean jsHighlightEnabled = false; 058 059 /** 060 * TODO apparent dup WebDriverITBase.DEFAULT_WAIT_SEC 061 * TODO parametrize for JVM Arg 062 * 30 Seconds 063 */ 064 public static int DEFAULT_IMPLICIT_WAIT_TIME = 30; 065 066 /** 067 * false 068 * TODO upgrade to config via JVM param. 069 */ 070 public static final boolean JGROWL_ERROR_FAILURE = false; 071 072 /** 073 * green 074 */ 075 public static final String JS_HIGHLIGHT_BACKGROUND = "#66FF33"; 076 077 /** 078 * green 079 */ 080 public static final String JS_HIGHLIGHT_BOARDER = "#66FF33"; 081 082 /** 083 * 400 milliseconds 084 */ 085 public static final int JS_HIGHLIGHT_MS = 400; 086 087 /** 088 * -Dremote.driver.highlight.ms= 089 */ 090 public static final String JS_HIGHLIGHT_MS_PROPERTY = "remote.driver.highlight.ms"; 091 092 /** 093 * -Dremote.driver.highlight=true to enable highlighting of elements as selenium runs 094 */ 095 public static final String JS_HIGHLIGHT_PROPERTY = "remote.driver.highlight"; 096 097 /** 098 * -Dremote.driver.highlight.input= 099 */ 100 public static final String JS_HIGHLIGHT_INPUT_PROPERTY = "remote.driver.highlight.input"; 101 102 /** 103 * TODO introduce SHORT_IMPLICIT_WAIT_TIME with param in WebDriverITBase 104 * TODO parametrize for JVM Arg 105 * 1 Second 106 */ 107 public static int SHORT_IMPLICIT_WAIT_TIME = 1; 108 109 /** 110 * Selenium's webdriver.chrome.driver parameter, you can set -Dwebdriver.chrome.driver= or Rice's REMOTE_PUBLIC_CHROME 111 */ 112 public static final String WEBDRIVER_CHROME_DRIVER = "webdriver.chrome.driver"; 113 114 /** 115 * Set -Dremote.jgrowl.enabled= 116 */ 117 public static final String REMOTE_JGROWL_ENABLED = "remote.jgrowl.enabled"; 118 119 /** 120 * Set -Dremote.login.uif=KNS to use old login screen. Default value = KRAD 121 */ 122 public static final String REMOTE_LOGIN_UIF = "remote.login.uif"; 123 124 /** 125 * Set -Dremote.public.chrome= or WEBDRIVER_CHROME_DRIVER 126 */ 127 public static final String REMOTE_PUBLIC_CHROME = "remote.public.chrome"; 128 129 /** 130 * Time to wait for the URL used in setup to load. Sometimes this is the first hit on the app and it needs a bit 131 * longer than any other. 120 Seconds. 132 * TODO parametrize for JVM Arg 133 */ 134 public static final int SETUP_URL_LOAD_WAIT_SECONDS = 120; 135 136 /** 137 * local proxy used for running tests thru jmeter. 138 * Include host name and port number. Example: localhost:7777 139 */ 140 public static final String PROXY_HOST_PROPERTY = "remote.public.proxy"; 141 142 /** 143 * Setup the WebDriver test, login, and load the given web page 144 * 145 * @param username 146 * @param url 147 * @return driver 148 * @throws Exception 149 */ 150 public static WebDriver setUp(String username, String url) throws Exception { 151 return setUp(username, url, null, null); 152 } 153 154 /** 155 * Setup the WebDriver test, login, and load the given web page 156 * 157 * @param username 158 * @param url 159 * @param className 160 * @param testName 161 * @return driver 162 * @throws Exception 163 */ 164 public static WebDriver setUp(String username, String url, String className, String testName) throws Exception { 165 if ("true".equals(System.getProperty(REMOTE_JGROWL_ENABLED, "false"))) { 166 jGrowlEnabled = true; 167 } 168 169 if ("true".equals(System.getProperty(JS_HIGHLIGHT_PROPERTY, "false"))) { 170 jsHighlightEnabled = true; 171 if (System.getProperty(JS_HIGHLIGHT_INPUT_PROPERTY) != null) { 172 InputStream in = WebDriverUtil.class.getResourceAsStream(System.getProperty(JS_HIGHLIGHT_INPUT_PROPERTY)); 173 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 174 String line = null; 175 List<String> lines = new LinkedList<String>(); 176 while ((line = reader.readLine()) != null) { 177 lines.add(line); 178 } 179 } 180 } 181 182 WebDriver driver = null; 183 if (System.getProperty(SauceLabsWebDriverHelper.REMOTE_DRIVER_SAUCELABS_PROPERTY) == null) { 184 driver = getWebDriver(); 185 } else { 186 SauceLabsWebDriverHelper saucelabs = new SauceLabsWebDriverHelper(); 187 saucelabs.setUp(className, testName); 188 driver = saucelabs.getDriver(); 189 } 190 191 driver.manage().timeouts().implicitlyWait(SETUP_URL_LOAD_WAIT_SECONDS, TimeUnit.SECONDS); 192 193 if (!System.getProperty(SauceLabsWebDriverHelper.SAUCE_BROWSER_PROPERTY,"ff").equals("opera")) { 194 driver.manage().window().maximize(); 195 } 196 197 // TODO Got into the situation where the first url doesn't expect server, but all others do. Readdress once 198 // the NavIT WDIT conversion has been completed. 199 if (!url.startsWith("http")) { 200 url = ITUtil.getBaseUrlString() + url; 201 } 202 203 driver.get(url); 204 driver.manage().timeouts().implicitlyWait(DEFAULT_IMPLICIT_WAIT_TIME, TimeUnit.SECONDS); 205 return driver; 206 } 207 208 /** 209 * 210 * @param passed 211 * @param sessionId 212 * @param testParam 213 * @param userParam 214 * @throws Exception 215 */ 216 public static void tearDown(boolean passed, String sessionId, String testParam, String userParam) throws Exception { 217 218 if (System.getProperty(SauceLabsWebDriverHelper.REMOTE_DRIVER_SAUCELABS_PROPERTY) != null) { 219 SauceLabsWebDriverHelper.tearDown(passed, sessionId, System.getProperty(SauceLabsWebDriverHelper.SAUCE_USER_PROPERTY), 220 System.getProperty(SauceLabsWebDriverHelper.SAUCE_KEY_PROPERTY)); 221 } 222 223 if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) { 224 ITUtil.getHTML(ITUtil.prettyHttp(System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" 225 + testParam + "&user=" + userParam)); 226 } 227 } 228 229 /** 230 * 231 * @param testParam 232 * @return 233 */ 234 public static String determineUser(String testParam) { 235 String user = null; 236 237 if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USER_PROPERTY) != null) { 238 return System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USER_PROPERTY); 239 } else if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) { // deprecated 240 String userResponse = ITUtil.getHTML(ITUtil.prettyHttp(System.getProperty( 241 WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + testParam.trim())); 242 return userResponse.substring(userResponse.lastIndexOf(":") + 2, userResponse.lastIndexOf("\"")); 243 } 244 245 return user; 246 } 247 248 /*** 249 * @link ITUtil#checkForIncidentReport 250 * @param driver 251 * @param locator 252 * @param message 253 */ 254 public static void checkForIncidentReport(WebDriver driver, String locator, Failable failable, 255 String message) { 256 ITUtil.checkForIncidentReport(driver.getPageSource(), locator, failable, message); 257 } 258 259 /** 260 * @link http://code.google.com/p/chromedriver/downloads/list 261 * @link #REMOTE_PUBLIC_CHROME 262 * @link #WEBDRIVER_CHROME_DRIVER 263 * @link ITUtil#HUB_DRIVER_PROPERTY 264 * @return chromeDriverService 265 */ 266 public static ChromeDriverService chromeDriverCreateCheck() { 267 String driverParam = System.getProperty(ITUtil.HUB_DRIVER_PROPERTY); 268 // TODO can the saucelabs driver stuff be leveraged here? 269 if (driverParam != null && "chrome".equals(driverParam.toLowerCase())) { 270 if (System.getProperty(WEBDRIVER_CHROME_DRIVER) == null) { 271 if (System.getProperty(REMOTE_PUBLIC_CHROME) != null) { 272 System.setProperty(WEBDRIVER_CHROME_DRIVER, System.getProperty(REMOTE_PUBLIC_CHROME)); 273 } 274 } 275 try { 276 ChromeDriverService chromeDriverService = new ChromeDriverService.Builder() 277 .usingDriverExecutable(new File(System.getProperty(WEBDRIVER_CHROME_DRIVER))) 278 .usingAnyFreePort() 279 .build(); 280 return chromeDriverService; 281 } catch (Throwable t) { 282 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) ; 283 } 284 } 285 return null; 286 } 287 288 public static void jGrowl(WebDriver driver, String jGrowlHeader, boolean sticky, String message, Throwable t) { 289 if (jGrowlEnabled) { // check if jGrowl is enabled to skip over the stack trace extraction if it is not. 290 jGrowl(driver, jGrowlHeader, sticky, message + " " + t.getMessage() + "\n" + ExceptionUtils.getStackTrace(t)); 291 } 292 } 293 294 public static void jGrowl(WebDriver driver, String jGrowlHeader, boolean sticky, String message) { 295 if (jGrowlEnabled) { 296 try { 297 String javascript="jQuery.jGrowl('" + message + "' , {sticky: " + sticky + ", header : '" + jGrowlHeader + "'});"; 298 ((JavascriptExecutor) driver).executeScript(javascript); 299 } catch (Throwable t) { 300 jGrowlException(t); 301 } 302 } 303 } 304 305 public static void jGrowlException(Throwable t) { 306 String failMessage = t.getMessage() + "\n" + ExceptionUtils.getStackTrace(t); 307 System.out.println("jGrowl failure " + failMessage); 308 if (JGROWL_ERROR_FAILURE) { 309 SeleneseTestBase.fail(failMessage); 310 } 311 } 312 313 public static void highlightElement(WebDriver webDriver, WebElement webElement) { 314 if (jsHighlightEnabled) { 315 try { 316// System.out.println("highlighting " + webElement.toString() + " on url " + webDriver.getCurrentUrl()); 317 JavascriptExecutor js = (JavascriptExecutor) webDriver; 318 js.executeScript("element = arguments[0];\n" 319 + "originalStyle = element.getAttribute('style');\n" 320 + "element.setAttribute('style', originalStyle + \"; background: " 321 + JS_HIGHLIGHT_BACKGROUND + "; border: 2px solid " + JS_HIGHLIGHT_BOARDER + ";\");\n" 322 + "setTimeout(function(){\n" 323 + " element.setAttribute('style', originalStyle);\n" 324 + "}, " + System.getProperty(JS_HIGHLIGHT_MS_PROPERTY, JS_HIGHLIGHT_MS + "") + ");", webElement); 325 } catch (Throwable t) { 326 System.out.println("Throwable during javascript highlight element"); 327 t.printStackTrace(); 328 } 329 } 330 } 331 332 333 /** 334 * remote.public.driver set to chrome or firefox (null assumes firefox) 335 * if remote.public.hub is set a RemoteWebDriver is created (Selenium Grid) 336 * if proxy.host is set, the web driver is setup to use a proxy 337 * @return WebDriver or null if unable to create 338 */ 339 public static WebDriver getWebDriver() { 340 String driverParam = System.getProperty(ITUtil.HUB_DRIVER_PROPERTY); 341 String hubParam = System.getProperty(ITUtil.HUB_PROPERTY); 342 String proxyParam = System.getProperty(PROXY_HOST_PROPERTY); 343 344 // setup proxy if specified as VM Arg 345 DesiredCapabilities capabilities = new DesiredCapabilities(); 346 WebDriver webDriver = null; 347 if (StringUtils.isNotEmpty(proxyParam)) { 348 capabilities.setCapability(CapabilityType.PROXY, new Proxy().setHttpProxy(proxyParam)); 349 } 350 351 if (hubParam == null) { 352 if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) { 353 FirefoxProfile profile = new FirefoxProfile(); 354 profile.setEnableNativeEvents(false); 355 capabilities.setCapability(FirefoxDriver.PROFILE, profile); 356 return new FirefoxDriver(capabilities); 357 } else if ("chrome".equalsIgnoreCase(driverParam)) { 358 return new ChromeDriver(capabilities); 359 } else if ("safari".equals(driverParam)) { 360 System.out.println("SafariDriver probably won't work, if it does please contact Erik M."); 361 return new SafariDriver(capabilities); 362 } 363 } else { 364 try { 365 if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) { 366 return new RemoteWebDriver(new URL(ITUtil.getHubUrlString()), DesiredCapabilities.firefox()); 367 } else if ("chrome".equalsIgnoreCase(driverParam)) { 368 return new RemoteWebDriver(new URL(ITUtil.getHubUrlString()), DesiredCapabilities.chrome()); 369 } 370 } catch (MalformedURLException mue) { 371 System.out.println(ITUtil.getHubUrlString() + " " + mue.getMessage()); 372 mue.printStackTrace(); 373 } 374 } 375 return null; 376 } 377 378 /** 379 * Logs in using the KRAD Login Page 380 * If the JVM arg remote.autologin is set, auto login as admin will not be done. 381 * @param driver 382 * @param userName 383 * @param failable 384 * @throws InterruptedException 385 */ 386 public static void kradLogin(WebDriver driver, String userName, Failable failable) throws InterruptedException { 387 driver.findElement(By.name("login_user")).clear(); 388 driver.findElement(By.name("login_user")).sendKeys(userName); 389 driver.findElement(By.id("Rice-LoginButton")).click(); 390 Thread.sleep(1000); 391 String contents = driver.getPageSource(); 392 ITUtil.failOnInvalidUserName(userName, contents, failable); 393 ITUtil.checkForIncidentReport(driver.getPageSource(), "Krad Login", failable, "Krad Login failure"); 394 } 395 396 /** 397 * Logs into the Rice portal using the KNS Style Login Page. 398 * @param driver 399 * @param userName 400 * @param failable 401 * @throws InterruptedException 402 */ 403 public static void login(WebDriver driver, String userName, Failable failable) throws InterruptedException { 404 driver.findElement(By.name("__login_user")).clear(); 405 driver.findElement(By.name("__login_user")).sendKeys(userName); 406 driver.findElement(By.cssSelector("input[type=\"submit\"]")).click(); 407 Thread.sleep(1000); 408 String contents = driver.getPageSource(); 409 ITUtil.failOnInvalidUserName(userName, contents, failable); 410 ITUtil.checkForIncidentReport(driver.getPageSource(), "KNS Login", failable, "KNS Login failure"); 411 } 412 413 public static void loginKradOrKns(WebDriver driver, String user, Failable failable) throws InterruptedException {// login via either KRAD or KNS login page 414 if ("true".equalsIgnoreCase(System.getProperty(ITUtil.REMOTE_AUTOLOGIN_PROPERTY, "true"))) { 415 if (isKradLogin()){ 416 WebDriverUtil.kradLogin(driver, user, failable); 417 } else { 418 WebDriverUtil.login(driver, user, failable); 419 } 420 } 421 } 422 423 /** 424 * Use the KRAD Login Screen or the old KNS Login Screen 425 */ 426 public static boolean isKradLogin(){ 427 // check system property, default to KRAD 428 String loginUif = System.getProperty(REMOTE_LOGIN_UIF); 429 if (loginUif == null) { 430 loginUif = ITUtil.REMOTE_UIF_KRAD; 431 } 432 433 return (ITUtil.REMOTE_UIF_KRAD.equalsIgnoreCase(loginUif)); 434 } 435 436 protected static void selectFrameSafe(WebDriver driver, String locator) { 437 try { 438 driver.switchTo().frame(locator); 439 } catch (NoSuchFrameException nsfe) { 440 // don't fail 441 } 442 } 443 444 /** 445 * Wait for the given amount of seconds, for the given by, using the given driver. The message is displayed if the 446 * by cannot be found. No action is performed on the by, so it is possible that the by found is not visible or enabled. 447 * 448 * @param driver WebDriver 449 * @param waitSeconds int 450 * @param by By 451 * @param message String 452 * @throws InterruptedException 453 */ 454 public static WebElement waitFor(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException { 455 driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS); 456 Thread.sleep(1000); 457 WebElement element = driver.findElement(by); // NOTICE just the find, no action, so by is found, but might not be visible or enabled. 458 driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS); 459 return element; 460 } 461 462 /** 463 * Wait for the given amount of seconds, for the given by, using the given driver. The message is displayed if the 464 * by cannot be found. No action is performed on the by, so it is possible that the by found is not visible or enabled. 465 * 466 * @param driver WebDriver 467 * @param waitSeconds int 468 * @param by By 469 * @param message String 470 * @throws InterruptedException 471 */ 472 public static void waitFors(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException { 473 driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS); 474 Thread.sleep(1000); 475 driver.findElements(by); // NOTICE just the find, no action, so by is found, but might not be visible or enabled. 476 driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS); 477 } 478 479 480 public static void failOnMatchedJira(String contents, Failable failable) { 481 JiraAwareFailureUtil.failOnMatchedJira(contents, failable); 482 } 483 484 private static void failWithReportInfoForKim(String contents, String linkLocator, String message) { 485 final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message); 486 SeleneseTestBase.fail(kimIncidentReport); 487 } 488 489 private static String extractIncidentReportKim(String contents, String linkLocator, String message) { 490 String chunk = contents.substring(contents.indexOf("id=\"headerarea\""), contents.lastIndexOf("</div>") ); 491 String docIdPre = "type=\"hidden\" value=\""; 492 String docId = chunk.substring(chunk.indexOf(docIdPre) + docIdPre.length(), chunk.indexOf("\" name=\"documentId\"")); 493 494 String stackTrace = chunk.substring(chunk.lastIndexOf("name=\"displayMessage\""), chunk.length()); 495 String stackTracePre = "value=\""; 496 stackTrace = stackTrace.substring(stackTrace.indexOf(stackTracePre) + stackTracePre.length(), stackTrace.indexOf("name=\"stackTrace\"") - 2); 497 498 return "\nIncident report "+ message+ " navigating to "+ linkLocator + " Doc Id: "+ docId.trim()+ "\nStackTrace: "+ stackTrace.trim().replace(" at ",""); 499 } 500 501 private static void processIncidentReport(String contents, String linkLocator, Failable failable, String message) { 502 failOnMatchedJira(contents, failable); 503 504 if (contents.indexOf("Incident Feedback") > -1) { 505 failWithReportInfo(contents, linkLocator, message); 506 } 507 508 if (contents.indexOf("Incident Report") > -1) { // KIM incident report 509 failWithReportInfoForKim(contents, linkLocator, message); 510 } 511 512 SeleneseTestBase.fail("\nIncident report detected " + message + "\n Unable to parse out details for the contents that triggered exception: " + deLinespace( 513 contents)); 514 } 515 516 private static void failWithReportInfo(String contents, String linkLocator, String message) { 517 final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message); 518 SeleneseTestBase.fail(incidentReportInformation); 519 } 520 521 private static String extractIncidentReportInfo(String contents, String linkLocator, String message) { 522 String chunk = contents.substring(contents.indexOf("Incident Feedback"), contents.lastIndexOf("</div>") ); 523 String docId = chunk.substring(chunk.lastIndexOf("Document Id"), chunk.indexOf("View Id")); 524 docId = docId.substring(0, docId.indexOf("</span>")); 525 docId = docId.substring(docId.lastIndexOf(">") + 2, docId.length()); 526 527 String viewId = chunk.substring(chunk.lastIndexOf("View Id"), chunk.indexOf("Error Message")); 528 viewId = viewId.substring(0, viewId.indexOf("</span>")); 529 viewId = viewId.substring(viewId.lastIndexOf(">") + 2, viewId.length()); 530 531 String stackTrace = chunk.substring(chunk.lastIndexOf("(only in dev mode)"), chunk.length()); 532 stackTrace = stackTrace.substring(stackTrace.indexOf("<span id=\"") + 3, stackTrace.length()); 533 stackTrace = stackTrace.substring(stackTrace.indexOf("\">") + 2, stackTrace.indexOf("</span>")); 534 535 return "\nIncident report "+ message+ " navigating to "+ linkLocator+ " : View Id: "+ viewId.trim()+ " Doc Id: "+ docId.trim()+ "\nStackTrace: "+ stackTrace.trim().replace(" at ", ""); 536 } 537 538 public static String deLinespace(String contents) { 539 while (contents.contains("\n\n")) { 540 contents = contents.replaceAll("\n\n", "\n"); 541 } 542 543 return contents; 544 } 545}