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