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