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 }