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