View Javadoc
1   /*
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package edu.samplu.common;
17  
18  import com.thoughtworks.selenium.SeleneseTestBase;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.commons.lang3.exception.ExceptionUtils;
21  import org.openqa.selenium.By;
22  import org.openqa.selenium.JavascriptExecutor;
23  import org.openqa.selenium.NoSuchFrameException;
24  import org.openqa.selenium.Proxy;
25  import org.openqa.selenium.WebDriver;
26  import org.openqa.selenium.WebElement;
27  import org.openqa.selenium.chrome.ChromeDriver;
28  import org.openqa.selenium.chrome.ChromeDriverService;
29  import org.openqa.selenium.firefox.FirefoxDriver;
30  import org.openqa.selenium.firefox.FirefoxProfile;
31  import org.openqa.selenium.remote.CapabilityType;
32  import org.openqa.selenium.remote.DesiredCapabilities;
33  import org.openqa.selenium.remote.RemoteWebDriver;
34  import org.openqa.selenium.safari.SafariDriver;
35  
36  import java.io.BufferedReader;
37  import java.io.File;
38  import java.io.InputStream;
39  import java.io.InputStreamReader;
40  import java.net.MalformedURLException;
41  import java.net.URL;
42  import java.util.LinkedList;
43  import java.util.List;
44  import java.util.concurrent.TimeUnit;
45  
46  /**
47   * The goal of the WebDriverUtil class is to invert the dependencies on WebDriver from WebDriverLegacyITBase for reuse
48   * without having to extend WebDriverLegacyITBase.  For the first example see waitFor
49   *
50   * @see WebDriverLegacyITBase
51   * @author Kuali Rice Team (rice.collab@kuali.org)
52   */
53  public class WebDriverUtil {
54  
55      public static boolean jGrowlEnabled = false;
56  
57      public static boolean jsHighlightEnabled = false;
58  
59      /**
60       * TODO apparent dup WebDriverITBase.DEFAULT_WAIT_SEC
61       * TODO parametrize for JVM Arg
62       * 30 Seconds
63       */
64      public static int DEFAULT_IMPLICIT_WAIT_TIME = 30;
65  
66      /**
67       * false
68       * TODO upgrade to config via JVM param.
69       */
70      public static final boolean JGROWL_ERROR_FAILURE = false;
71  
72      /**
73       * green
74       */
75      public static final String JS_HIGHLIGHT_BACKGROUND = "#66FF33";
76  
77      /**
78       * green
79       */
80      public static final String JS_HIGHLIGHT_BOARDER = "#66FF33";
81  
82      /**
83       * 400 milliseconds
84       */
85      public static final int JS_HIGHLIGHT_MS = 400;
86  
87      /**
88       * -Dremote.driver.highlight.ms=
89       */
90      public static final String JS_HIGHLIGHT_MS_PROPERTY = "remote.driver.highlight.ms";
91  
92      /**
93       * -Dremote.driver.highlight=true to enable highlighting of elements as selenium runs
94       */
95      public static final String JS_HIGHLIGHT_PROPERTY = "remote.driver.highlight";
96  
97      /**
98       * -Dremote.driver.highlight.input=
99       */
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 }