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