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