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 }