View Javadoc
1   /**
2    * Copyright 2005-2014 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 org.kuali.rice.testtools.selenium;
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.Alert;
22  import org.openqa.selenium.By;
23  import org.openqa.selenium.Dimension;
24  import org.openqa.selenium.JavascriptExecutor;
25  import org.openqa.selenium.NoSuchFrameException;
26  import org.openqa.selenium.Proxy;
27  import org.openqa.selenium.WebDriver;
28  import org.openqa.selenium.WebElement;
29  import org.openqa.selenium.chrome.ChromeDriver;
30  import org.openqa.selenium.chrome.ChromeDriverService;
31  import org.openqa.selenium.firefox.FirefoxDriver;
32  import org.openqa.selenium.firefox.FirefoxProfile;
33  import org.openqa.selenium.remote.CapabilityType;
34  import org.openqa.selenium.remote.DesiredCapabilities;
35  import org.openqa.selenium.remote.RemoteWebDriver;
36  import org.openqa.selenium.safari.SafariDriver;
37  
38  import java.io.BufferedReader;
39  import java.io.File;
40  import java.io.InputStream;
41  import java.io.InputStreamReader;
42  import java.net.MalformedURLException;
43  import java.net.URL;
44  import java.text.SimpleDateFormat;
45  import java.util.Calendar;
46  import java.util.Date;
47  import java.util.LinkedList;
48  import java.util.List;
49  import java.util.concurrent.TimeUnit;
50  
51  /**
52   * <p>
53   * The goal of the WebDriverUtils class is to invert the dependencies on WebDriver from {@see WebDriverLegacyITBase} for reuse
54   * without having to extend WebDriverLegacyITBase.
55   * </p><p>
56   * For compatibility with {@see JiraAwareFailureUtils}, external test framework asserts and fails should not be called from
57   * WebDriverUtils, instead use {@see JiraAwareAftBase}.
58   * </p><p>
59   * For the first example see waitFor
60   * </p>
61   * @see WebDriverLegacyITBase
62   * @author Kuali Rice Team (rice.collab@kuali.org)
63   */
64  public class WebDriverUtils {
65  
66      protected static SauceLabsWebDriverHelper saucelabs;
67  
68      public static boolean jGrowlEnabled = false;
69  
70      public static boolean jsHighlightEnabled = false;
71  
72      /**
73       * http://localhost:8080/kr-dev
74       */
75      public static final String DEFAULT_BASE_URL = "http://localhost:8080/kr-dev";
76  
77      /**
78       * http://localhost:8080/krad-dev
79       */
80      public static final String DEFAULT_BASE_URL_KRAD = "http://localhost:8080/krad-dev";
81  
82      /**
83       * <p>
84       * Set to true to not close the browser after the test has run.
85       * </p><p>
86       * -Dremote.driver.dontTearDown=true
87       * </p>
88       */
89      public static final String DONT_TEAR_DOWN_PROPERTY = "remote.driver.dontTearDown";
90  
91      /**
92       * <p>
93       * Set to true to not close the browser after the test has if the test failed.
94       * </p><p>
95       * -Dremote.driver.dontTearDownOnFailure=true
96       * </p>
97       */
98      public static final String DONT_TEAR_DOWN_ON_FAILURE_PROPERTY = "remote.driver.dontTearDownOnFailure";
99  
100     /**
101      * remote.public.driver
102      */
103     public static final String HUB_DRIVER_PROPERTY = "remote.public.driver";
104 
105     /**
106      * For use when running Selenium tests through a Selenium Hub.
107      * -Dremote.public.hub=
108      */
109     public static final String HUB_PROPERTY = "remote.public.hub";
110 
111     /**
112      * http://localhost:4444/wd/hub
113      */
114     public static final String HUB_URL_PROPERTY = "http://localhost:4444/wd/hub";
115 
116     /**
117      * wait Methods inter loop sleep period, default of 1000 Milliseconds.
118      * TODO parametrize for JVM Arg
119      */
120     public static int IMPLICIT_WAIT_TIME_LOOP_MS = 1000;
121 
122     /**
123      * <p>
124      * {@see IMPLICIT_WAIT_TIME_SECONDS_DEFAULT} to configure, default 30 seconds.
125      * </p><p>
126      * In code don't use this variable but call {@see configuredImplicityWait} to get the configured value.
127      * </p>
128      */
129     public static int IMPLICIT_WAIT_TIME_SECONDS_DEFAULT = 30;
130 
131     /**
132      * If true tests will fail on jGrowl errors, default of false
133      * TODO upgrade to config via JVM param.
134      */
135     public static final boolean JGROWL_ERROR_FAILURE = false;
136 
137     /**
138      * green (#66FF33)
139      */
140     public static final String JS_HIGHLIGHT_BACKGROUND = "#66FF33";
141 
142     /**
143      * green (#66FF33)
144      */
145     public static final String JS_HIGHLIGHT_BOARDER = "#66FF33";
146 
147     /**
148      * 400 milliseconds.
149      */
150     public static final int JS_HIGHLIGHT_MS = 400;
151 
152     /**
153      * <p>
154      * {@see JS_HIGHLIGHT_MS} as default.
155      * </p><p>
156      * -Dremote.driver.highlight.ms=
157      * </p>
158      */
159     public static final String JS_HIGHLIGHT_MS_PROPERTY = "remote.driver.highlight.ms";
160 
161     /**
162      * <p>
163      * Highlighting of elements as selenium runs.
164      * </p><p>
165      * -Dremote.driver.highlight=true
166      * </p>
167      */
168     public static final String JS_HIGHLIGHT_PROPERTY = "remote.driver.highlight";
169 
170     /**
171      * TODO: playback for javascript highlighting.
172      *
173      * -Dremote.driver.highlight.input=
174      */
175     public static final String JS_HIGHLIGHT_INPUT_PROPERTY = "remote.driver.highlight.input";
176 
177     /**
178      * <p>
179      * Local proxy used for running tests thru jmeter.
180      * </p><p>
181      * Include host name and port number. Example: localhost:7777
182      * </p><p>
183      * -Dremote.public.proxy=
184      * </p>
185      */
186     public static final String PROXY_HOST_PROPERTY = "remote.public.proxy";
187 
188     /**
189      * <p>
190      * Skip automatice login if set to anything other than true/
191      * </p><p>
192      * -Dremote.autologin=false
193      * </p>
194      */
195     public static final String REMOTE_AUTOLOGIN_PROPERTY = "remote.autologin";
196 
197     /**
198      * <p>
199      * Set to true to enable jGrowl test messages.
200      * </p><p>
201      * When enabled, jGrowl messages will be sent when clicking on buttons and links identified by their text.
202      * </p><p>
203      * -Dremote.jgrowl.enabled=true
204      * </p>
205      */
206     public static final String REMOTE_JGROWL_ENABLED = "remote.jgrowl.enabled";
207 
208     /**
209      * Set -Dremote.login.uif=KNS to use old login screen.  Default value = KRAD
210      */
211     public static final String REMOTE_LOGIN_UIF = "remote.login.uif";
212 
213     /**
214      * Set -Dremote.property.file= to load proprties from file
215      */
216     public static final String REMOTE_PROPERTIES_PROPERTY = "remote.property.file";
217 
218     /**
219      * Set -Dremote.public.chrome= or WEBDRIVER_CHROME_DRIVER
220      */
221     public static final String REMOTE_PUBLIC_CHROME = "remote.public.chrome";
222 
223     /**
224      * -Dremote.public.url=
225      */
226     public static final String REMOTE_PUBLIC_URL_PROPERTY = "remote.public.url";
227 
228     /**
229      * <p>
230      * Set -Dremote.public.wait.seconds to override DEFAULT_WAIT_SEC.
231      * </p><p>
232      * {@see IMPLICIT_WAIT_TIME_SECONDS_DEFAULT}
233      * </p>
234      */
235     public static final String REMOTE_PUBLIC_WAIT_SECONDS_PROPERTY = "remote.public.wait.seconds";
236 
237     /**
238      * Set -Dremote.public.user= to the username to login as
239      */
240     public static final String REMOTE_PUBLIC_USER_PROPERTY = "remote.public.user";
241 
242     /**
243      * You probably don't want to really be using a userpool, set -Dremote.public.userpool= to base url if you must.
244      */
245     public static final String REMOTE_PUBLIC_USERPOOL_PROPERTY = "remote.public.userpool";
246 
247     /**
248      * <p>
249      * Time to wait for the URL used in setup to load, 120 seconds by default.
250      * </p><p>
251      * Sometimes this is the first hit on the app and it needs a bit longer than any other.
252      * </p><p>
253      * TODO parametrize for JVM Arg
254      * <p>
255      */
256     public static final int SETUP_URL_LOAD_WAIT_SECONDS = 120;
257 
258     /**
259      * Selenium's webdriver.chrome.driver parameter, you can set -Dwebdriver.chrome.driver= or Rice's REMOTE_PUBLIC_CHROME.
260      */
261     public static final String WEBDRIVER_CHROME_DRIVER = "webdriver.chrome.driver";
262 
263     /**
264      * Setup the WebDriver test, login, and load the given web page
265      *
266      * @param className
267      * @param testName
268      * @return driver
269      * @throws Exception
270      */
271     public static WebDriver setUp(String className, String testName) throws Exception {
272         if ("true".equals(System.getProperty(REMOTE_JGROWL_ENABLED, "false"))) {
273             jGrowlEnabled = true;
274         }
275 
276         if ("true".equals(System.getProperty(JS_HIGHLIGHT_PROPERTY, "false"))) {
277             jsHighlightEnabled = true;
278             if (System.getProperty(JS_HIGHLIGHT_INPUT_PROPERTY) != null) {
279                 InputStream in = WebDriverUtils.class.getResourceAsStream(System.getProperty(JS_HIGHLIGHT_INPUT_PROPERTY));
280                 BufferedReader reader = new BufferedReader(new InputStreamReader(in));
281                 String line = null;
282                 List<String> lines = new LinkedList<String>();
283                 while ((line = reader.readLine()) != null) {
284                     lines.add(line);
285                 }
286             }
287         }
288 
289         WebDriver driver = null;
290         if (System.getProperty(SauceLabsWebDriverHelper.REMOTE_DRIVER_SAUCELABS_PROPERTY) == null) {
291             driver = getWebDriver();
292         } else {
293             saucelabs = new SauceLabsWebDriverHelper();
294             saucelabs.setUp(className, testName);
295             driver = saucelabs.getDriver();
296         }
297 
298         return driver;
299     }
300 
301     /**
302      *<p>
303      * Calls {@see SauceLabsWebDriverHelper#tearDown} if {@see #REMOTE_PUBLIC_USERPOOL_PROPERTY} is enabled, calls a user pool
304      * url with the given poolParamTest and poolParamUser.
305      *</p>
306      *
307      * @param passed used by {@see SauceLabsWebDriverHelper#tearDown} to record Saucelabs test status can be null if Saucelabs
308      * is not being used
309      * @param sessionId used by {@see SauceLabsWebDriverHelper#tearDown} to record Saucelabs sessionId status can be null if Saucelabs
310      * is not being used
311      * @param poolParamTest can be null unless a user pool is being used
312      * @param poolParamUser can be null unless a user pool is being used
313      * @throws Exception
314      */
315     public static void tearDown(boolean passed, String sessionId, String poolParamTest, String poolParamUser, String className, String testName) throws Exception {
316 
317         if (passed) {
318             System.out.println("Registering session passed " + sessionId);
319         } else {
320             System.out.println("Registering session failed " + sessionId);
321         }
322 
323         if (System.getProperty(SauceLabsWebDriverHelper.REMOTE_DRIVER_SAUCELABS_PROPERTY) != null) {
324             saucelabs.tearDown(passed, sessionId, className, testName);
325         }
326 
327         if (System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) {
328             AutomatedFunctionalTestUtils.getHTML(AutomatedFunctionalTestUtils.prettyHttp(System.getProperty(
329                     REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + poolParamTest + "&user=" + poolParamUser));
330         }
331     }
332 
333     /**
334      * <p>
335      * If an alert is present accept it print the alert text to System.out
336      * </p>
337      *
338      * @param driver to accept alert on
339      */
340     public static void acceptAlertIfPresent(WebDriver driver) {
341         if (WebDriverUtils.isAlertPresent(driver)) {
342             System.out.println("Alert present " + WebDriverUtils.alertText(driver));
343             try {
344                 alertAccept(driver);
345             } catch (Exception e) {
346                 // Don't fail on alert exception
347             }
348         }
349     }
350 
351     /**
352      * <p>
353      * Accept the javascript alert (clicking OK).
354      * </p>
355      *
356      * @param driver WebDriver to accept alert on
357      */
358     public static void alertAccept(WebDriver driver) {
359         Alert alert = driver.switchTo().alert();
360         jGrowl(driver, "AFT Step", false, "AFT Step: Accept Alert " + WebDriverUtils.alertText(driver));
361         alert.accept();
362     }
363 
364     /**
365      * <p>
366      * Dismiss the javascript alert (clicking Cancel).
367      * </p>
368      *
369      * @param driver WebDriver to dismiss alert on
370      */
371     public static void alertDismiss(WebDriver driver) {
372         Alert alert = driver.switchTo().alert();
373         jGrowl(driver, "AFT Step", false, "AFT Step: Dismiss Alert " + WebDriverUtils.alertText(driver));
374         alert.dismiss();
375     }
376 
377     /**
378      * <p>
379      * Return alert text.
380      * </p>
381      *
382      * @param driver to get alert text from
383      * @return alert text
384      */
385     public static String alertText(WebDriver driver) {
386         return driver.switchTo().alert().getText();
387     }
388 
389     /**
390      * <p>
391      * <a href="http://code.google.com/p/chromedriver/downloads/list">ChromeDriver downloads</a>, {@see #REMOTE_PUBLIC_CHROME},
392      * {@see #WEBDRIVER_CHROME_DRIVER}, and {@see #HUB_DRIVER_PROPERTY}
393      * </p>
394      *
395      * @return chromeDriverService
396      */
397     public static ChromeDriverService chromeDriverCreateCheck() {
398         String driverParam = System.getProperty(HUB_DRIVER_PROPERTY);
399         // TODO can the saucelabs driver stuff be leveraged here?
400         if (driverParam != null && "chrome".equals(driverParam.toLowerCase())) {
401             if (System.getProperty(WEBDRIVER_CHROME_DRIVER) == null) {
402                 if (System.getProperty(REMOTE_PUBLIC_CHROME) != null) {
403                     System.setProperty(WEBDRIVER_CHROME_DRIVER, System.getProperty(REMOTE_PUBLIC_CHROME));
404                 }
405             }
406             try {
407                 ChromeDriverService chromeDriverService = new ChromeDriverService.Builder()
408                         .usingDriverExecutable(new File(System.getProperty(WEBDRIVER_CHROME_DRIVER)))
409                         .usingAnyFreePort()
410                         .build();
411                 return chromeDriverService;
412             } catch (Throwable t) {
413                 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)   ;
414             }
415         }
416         return null;
417     }
418 
419     /**
420      * <p>
421      * Return the configured implicity wait seconds, {@see #REMOTE_PUBLIC_WAIT_SECONDS_PROPERTY} and {@see #IMPLICIT_WAIT_TIME_SECONDS_DEFAULT}.
422      * </p>
423      *
424      * @return seconds for implicity wait
425      */
426     public static int configuredImplicityWait() {
427         return Integer.parseInt(System.getProperty(REMOTE_PUBLIC_WAIT_SECONDS_PROPERTY, IMPLICIT_WAIT_TIME_SECONDS_DEFAULT + ""));
428     }
429 
430     /**
431      * <p>
432      * Remove double line spacing.
433      * </p>
434      *
435      * @param contents String to remove double line spacing from
436      * @return String with double line spacing removed.
437      */
438     public static String deLinespace(String contents) {
439         while (contents.contains("\n\n")) {
440             contents = contents.replaceAll("\n\n", "\n");
441         }
442 
443         return contents;
444     }
445 
446     /**
447      * <p>
448      * If {@see #REMOTE_PUBLIC_USER_PROPERTY} property is set, return its value, else if {@see #REMOTE_PUBLIC_USERPOOL_PROPERTY}
449      * is set use it to query the userpool service passing the testParam.
450      * </p>
451      * *
452      * @param testParam to use if using a user pool
453      * @return user
454      */
455     public static String determineUser(String testParam) {
456         String user = null;
457 
458         if (System.getProperty(REMOTE_PUBLIC_USER_PROPERTY) != null) {
459             return System.getProperty(REMOTE_PUBLIC_USER_PROPERTY);
460         } else if (System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) { // deprecated
461             String userResponse = AutomatedFunctionalTestUtils.getHTML(AutomatedFunctionalTestUtils.prettyHttp(
462                     System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + testParam.trim()));
463             return userResponse.substring(userResponse.lastIndexOf(":") + 2, userResponse.lastIndexOf("\""));
464         }
465 
466         return user;
467     }
468 
469     /**
470      * <p>
471      * Setting the JVM arg remote.driver.dontTearDown to y or t leaves the browser window open when the test has completed.
472      * <p></p>
473      * Valuable when debugging, updating, or creating new tests.  When implementing your own tearDown method rather than an
474      * inherited one, it is a common courtesy to include this check and not stop and shutdown the browser window to make it
475      * easy debug or update your test.
476      * </p>
477      *
478      * @return true if the dontTearDownProperty is not set.
479      */
480     public static boolean dontTearDownPropertyNotSet() {
481         return System.getProperty(DONT_TEAR_DOWN_PROPERTY) == null ||
482                 "f".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase()) ||
483                 "n".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase());
484     }
485 
486     /**
487      * Given the boolean parameter and depending on if {@see #DONT_TEAR_DOWN_ON_FAILURE_PROPERTY} is set to something other
488      * than n, don't tear down the browser window on a test failure.
489 
490      * @param passed
491      * @return
492      */
493     public static boolean dontTearDownOnFailure(boolean passed) {
494         if (!"n".equalsIgnoreCase(System.getProperty(DONT_TEAR_DOWN_ON_FAILURE_PROPERTY, "n"))) {
495             return passed;
496         }
497         return true;
498     }
499 
500     /**
501      * <p>
502      * Find Button by text.
503      * </p>
504      *
505      * @param driver to find button on
506      * @param buttonText text to find button by
507      * @return WebElement of button with button text
508      */
509     public static WebElement findButtonByText(WebDriver driver, String buttonText) {
510         return findElement(driver, By.xpath("//button[contains(text(), '" + buttonText + "')]"));
511     }
512 
513     /**
514      * <p>
515      * Find and highlight the WebElement using the given WebDriver and By.
516      * </p>
517      *
518      * @param driver driver to find on
519      * @param by selector to find
520      * @return
521      */
522     public static WebElement findElement(WebDriver driver, By by) {
523         WebElement found = driver.findElement(by);
524         WebDriverUtils.highlightElement(driver, found);
525         return found;
526     }
527 
528     /**
529      * <p>
530      * In order to run as a smoke test the ability to set the baseUrl via the JVM arg remote.public.url is required.
531      * </p><p>
532      * Trailing slashes are trimmed.  If the remote.public.url does not start with http:// it will be added.
533      * </p>
534      *
535      * @return http://localhost:8080/kr-dev by default else the value of remote.public.url
536      */
537     public static String getBaseUrlString() {
538         String baseUrl = System.getProperty(REMOTE_PUBLIC_URL_PROPERTY);
539         if (baseUrl == null) {
540             baseUrl = DEFAULT_BASE_URL;
541         }
542         baseUrl = AutomatedFunctionalTestUtils.prettyHttp(baseUrl);
543         return baseUrl;
544     }
545 
546     /**
547      * yyyy-MM-dd-HH-mm-ss
548      *
549      * @return Date formatted as yyyy-MM-dd-HH-mm-ss
550      */
551     public static String getDateTimeStampFormatted() {
552         Date now = Calendar.getInstance().getTime();
553         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
554         return sdf.format(now);
555     }
556 
557     /**
558      * <p>
559      * Return the WebElement that has the attribute name with the given value.
560      * </p>
561      *
562      * @param driver to get element from
563      * @param attributeName attribute name to find element by
564      * @param value for the attribute name to find element by
565      * @return WebElement
566      */
567     public static WebElement getElementByAttributeValue(WebDriver driver, String attributeName, String value){
568         return findElement(driver, By.cssSelector("[" + attributeName + "='" + value +"']"));
569     }
570 
571     /**
572      * <p>
573      * In order to run as a smoke test under selenium grid the ability to set the hubUrl via the JVM arg remote.public.hub is required.
574      * </p><p>
575      * Trailing slashes are trimmed.  If the remote.public.hub does not start with http:// it will be added.
576      * </p>
577      *
578      * @return http://localhost:4444/wd/hub by default else the value of remote.public.hub
579      */
580     public static String getHubUrlString() {
581         String hubUrl = System.getProperty(HUB_PROPERTY);
582         if (hubUrl == null) {
583             hubUrl = HUB_URL_PROPERTY;
584         }
585         hubUrl = AutomatedFunctionalTestUtils.prettyHttp(hubUrl);
586         if (!hubUrl.endsWith("/wd/hub")) {
587             hubUrl = hubUrl + "/wd/hub";
588         }
589         return hubUrl;
590     }
591 
592     /**
593      * <p>
594      * remote.public.driver set to chrome or firefox (null assumes firefox).
595      * </p><p>
596      * if remote.public.hub is set a RemoteWebDriver is created (Selenium Grid)
597      * if proxy.host is set, the web driver is setup to use a proxy
598      * </p>
599      *
600      * @return WebDriver or null if unable to create
601      */
602     public static WebDriver getWebDriver() {
603         String driverParam = System.getProperty(HUB_DRIVER_PROPERTY);
604         String hubParam = System.getProperty(HUB_PROPERTY);
605         String proxyParam = System.getProperty(PROXY_HOST_PROPERTY);
606 
607         // setup proxy if specified as VM Arg
608         DesiredCapabilities capabilities = new DesiredCapabilities();
609         WebDriver webDriver = null;
610         if (StringUtils.isNotEmpty(proxyParam)) {
611             capabilities.setCapability(CapabilityType.PROXY, new Proxy().setHttpProxy(proxyParam));
612         }
613 
614         if (hubParam == null) {
615             if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
616                 FirefoxProfile profile = new FirefoxProfile();
617                 profile.setEnableNativeEvents(false);
618                 capabilities.setCapability(FirefoxDriver.PROFILE, profile);
619                 return new FirefoxDriver(capabilities);
620             } else if ("chrome".equalsIgnoreCase(driverParam)) {
621                 return new ChromeDriver(capabilities);
622             } else if ("safari".equals(driverParam)) {
623                 System.out.println("SafariDriver probably won't work, if it does please contact Erik M.");
624                 return new SafariDriver(capabilities);
625             }
626         } else {
627             try {
628                 if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
629                     return new RemoteWebDriver(new URL(getHubUrlString()), DesiredCapabilities.firefox());
630                 } else if ("chrome".equalsIgnoreCase(driverParam)) {
631                     return new RemoteWebDriver(new URL(getHubUrlString()), DesiredCapabilities.chrome());
632                 }
633             } catch (MalformedURLException mue) {
634                 System.out.println(getHubUrlString() + " " + mue.getMessage());
635                 mue.printStackTrace();
636             }
637         }
638         return null;
639     }
640 
641     public static void highlightElement(WebDriver webDriver, By by) {
642         List<WebElement> elements = webDriver.findElements(by);
643         for (WebElement element : elements) {
644             WebDriverUtils.highlightElement(webDriver, element);
645         }
646     }
647 
648 
649     public static void highlightElements(WebDriver webDriver, List<WebElement> webElements) {
650         for (WebElement webElement: webElements) {
651             highlightElement(webDriver, webElement);
652         }
653     }
654 
655     /**
656      * <p>
657      * Highlight given WebElement.
658      * </p>
659      *
660      * @param webDriver to execute highlight on
661      * @param webElement to highlight
662      */
663     public static void highlightElement(WebDriver webDriver, WebElement webElement) {
664         if (jsHighlightEnabled && webElement != null) {
665             try {
666                 //                System.out.println("highlighting " + webElement.toString() + " on url " + webDriver.getCurrentUrl());
667                 JavascriptExecutor js = (JavascriptExecutor) webDriver;
668                 String jsHighlight = "element = arguments[0];\n"
669                         + "originalStyle = element.getAttribute('style');\n"
670                         + "element.setAttribute('style', originalStyle + \"; background: "
671                         + JS_HIGHLIGHT_BACKGROUND + "; border: 2px solid " + JS_HIGHLIGHT_BOARDER + ";\");\n"
672                         + "setTimeout(function(){\n"
673                         + "    element.setAttribute('style', originalStyle);\n"
674                         + "}, " + System.getProperty(JS_HIGHLIGHT_MS_PROPERTY, JS_HIGHLIGHT_MS + "") + ");";
675                 js.executeScript(jsHighlight, webElement);
676             } catch (Throwable t) {
677                 System.out.println("Throwable during javascript highlight element");
678                 t.printStackTrace();
679             }
680         }
681     }
682 
683     /**
684      * <p>
685      * Return true if an alert is present, false if not.
686      * </p>
687      *
688      * @param driver to check for presents of alert on
689      * @return true if there is an alert present, false if not
690      */
691     public static boolean isAlertPresent(WebDriver driver) {
692         try {
693             driver.switchTo().alert();
694             return true;
695         } catch (Exception e) {
696             return false;
697         }
698     }
699 
700     public static Boolean isTextPresent(WebDriver driver, String pageText, String text) {
701         boolean textPresent = Boolean.FALSE;
702         if (pageText.contains(text)) {
703             WebDriverUtils.highlightElement(driver, By.xpath("//*[contains(text(), '" + text + "')]"));
704             textPresent = Boolean.TRUE;
705         }
706         WebDriverUtils.jGrowl(driver, "Is Text Present?", false, "Is text '" + text + "' present?" + " " + textPresent);
707         return textPresent;
708     }
709 
710     /**
711      * <p>
712      * Display jGrowl.
713      * </p>
714      *
715      * @param driver WebDriver to execute jGrowl on
716      * @param jGrowlHeader header text for jGrowl
717      * @param sticky true to set the jGrowl to sticky, false for not sticky
718      * @param message message to display in the jGrowl
719      * @param throwable message and stacktrace to included in jGrowl
720      */
721     public static void jGrowl(WebDriver driver, String jGrowlHeader, boolean sticky, String message, Throwable throwable) {
722         if (jGrowlEnabled) { // check if jGrowl is enabled to skip over the stack trace extraction if it is not.
723             jGrowl(driver, jGrowlHeader, sticky, message + " " + throwable.getMessage() + "\n" + ExceptionUtils.getStackTrace(throwable));
724         }
725     }
726 
727     /**
728      * <p>
729      * Display jGrowl.
730      * </p>
731      *
732      * @param driver WebDriver to execute jGrowl on
733      * @param jGrowlHeader header text for jGrowl
734      * @param sticky true to set the jGrowl to sticky, false for not sticky
735      * @param message message to display in the jGrowl
736      */
737     public static void jGrowl(WebDriver driver, String jGrowlHeader, boolean sticky, String message) {
738         stepMessage(message);
739         if (jGrowlEnabled) {
740             String javascript="jQuery.jGrowl('" + message + "' , {sticky: " + sticky + ", header : '" + jGrowlHeader + "'});";
741             try {
742                 ((JavascriptExecutor) driver).executeScript(javascript);
743             } catch (Throwable t) {
744                 jGrowlException(t, javascript);
745             }
746         }
747     }
748 
749     /**
750      * <p>
751      * Print jGrowl Exception to System.out, if {@see #JGROWL_ERROR_FAILURE} is set to true, fail.
752      * </p>
753      *
754      * @param throwable message and stack trace to print and if configured fail with
755      */
756     public static void jGrowlException(Throwable throwable, String jGrowlJavascript) {
757         String failMessage = jGrowlJavascript + " failed with " + throwable.getMessage() + "\n"
758                 + ExceptionUtils.getStackTrace(throwable);
759         System.out.println("jGrowl failure " + failMessage);
760         if (JGROWL_ERROR_FAILURE) {
761             SeleneseTestBase.fail(failMessage); // SeleneseTestBase fail okay here as jGrowl failures are not Jira worthy yet
762         }
763     }
764 
765     public static void openTestUrl(WebDriver driver, String testUrl) {
766         driver.manage().timeouts().implicitlyWait(WebDriverUtils.SETUP_URL_LOAD_WAIT_SECONDS, TimeUnit.SECONDS);
767 
768         driver.get(testUrl);
769         if (!System.getProperty(SauceLabsWebDriverHelper.SAUCE_BROWSER_PROPERTY,"ff").equals("opera")) {
770             driver.manage().window().maximize();
771             //            driver.manage().window().setSize(new Dimension(800,600));
772         }
773 
774         WebDriverUtils.jGrowl(driver, "Open URL", false, "Open " + testUrl);
775 
776         driver.manage().timeouts().implicitlyWait(WebDriverUtils.configuredImplicityWait(), TimeUnit.SECONDS);
777     }
778 
779     /**
780      * <p>
781      * Select frame defined by locator without throwing an Exception if it doesn't exist.
782      * </p>
783      *
784      * @param driver to select frame on
785      * @param locator to identify frame to select
786      */
787     public static void selectFrameSafe(WebDriver driver, String locator) {
788         try {
789             driver.switchTo().frame(locator);
790         } catch (NoSuchFrameException nsfe) {
791             // don't fail
792         }
793     }
794 
795     public static void stepMessage(String message) {
796         System.out.println("AFT Step: " + message);
797     }
798 
799     /**
800      * <p>
801      * Return the WebElement that has the attribute name with the given value within the given seconds to wait.
802      * </p>
803      *
804      * @param driver to get element from
805      * @param attribute name to find element by
806      * @param attributeValue for the attribute name to find element by
807      * @param waitSeconds number of seconds to wait
808      * @return WebElement
809      * @throws InterruptedException
810      */
811     public static WebElement waitAndGetElementByAttributeValue(WebDriver driver, String attribute, String attributeValue, int waitSeconds) throws InterruptedException {
812         // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp
813         //        driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
814         //        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
815 
816         driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
817 
818         boolean failed = false;
819 
820         for (int second = 0;; second++) {
821             Thread.sleep(1000);
822             if (second >= waitSeconds) {
823                 failed = true;
824             }
825             try {
826                 if (failed || (getElementByAttributeValue(driver, attribute, attributeValue) != null)) {
827                     break;
828                 }
829             } catch (Exception e) {}
830         }
831 
832         WebElement element = getElementByAttributeValue(driver, attribute, attributeValue);
833         driver.manage().timeouts().implicitlyWait(WebDriverUtils.configuredImplicityWait(), TimeUnit.SECONDS);
834         return element;
835     }
836 
837     public static void waitToAcceptAlert(WebDriver driver, int waitSeconds, String message) throws InterruptedException {
838         driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS);
839 
840         boolean failed = false;
841 
842         for (int second = 0;; second++) {
843             Thread.sleep(1000);
844             if (second >= waitSeconds) {
845                 failed = true;
846             }
847             try {
848                 if (failed) {
849                     break;
850                 } else if (isAlertPresent(driver)) {
851                     acceptAlertIfPresent(driver);
852                     break;
853                 }
854             } catch (Exception e) {}
855         }
856 
857         driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS);
858     }
859 
860     /**
861      * <p>
862      * Wait for the given amount of seconds, for the given by, using the given driver.  The message is displayed if the
863      * by cannot be found.  No action is performed on the by, so it is possible that the by found is not visible or enabled.
864      * </p>
865      *
866      * @param driver WebDriver to wait on
867      * @param waitSeconds seconds to wait
868      * @param by By to wait for
869      * @param message to display if by is not found in waitSeconds
870      * @throws InterruptedException
871      */
872     public static WebElement waitFor(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException {
873         // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp
874         //        driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
875         //        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
876 
877         driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS);
878 
879         boolean failed = false;
880         WebElement element = null;
881 
882         for (int second = 0;; second++) {
883             Thread.sleep(1000);
884             if (second >= waitSeconds) {
885                 failed = true;
886             }
887             try {
888                 if (failed) {
889                     break;
890                 } else if ((driver.findElements(by)).size() > 0) {
891                     element = findElement(driver, by);  // NOTICE just the find, no action, so by is found, but might not be visible or enabled.
892                     highlightElement(driver, element);
893                     break;
894                 }
895             } catch (Exception e) {}
896         }
897 
898         driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS);
899         return element;
900     }
901 
902     /**
903      * <p>
904      * Wait for WebElements.
905      * </p>
906      *
907      * @param driver WebDriver to wait on
908      * @param by By to wait for
909      * @return
910      * @throws InterruptedException
911      */
912     public static List<WebElement> waitFors(WebDriver driver, By by) throws InterruptedException {
913         return waitFors(driver, configuredImplicityWait(), by, "");
914     }
915 
916     /**
917      * <p>
918      * Wait for WebElements.
919      * </p>
920      *
921      * @param driver WebDriver to wait on
922      * @param by By to wait for
923      * @param message to display if by is not found in waitSeconds
924      * @return List of WebElements found
925      * @throws InterruptedException
926      */
927     public static List<WebElement> waitFors(WebDriver driver, By by, String message) throws InterruptedException {
928         return waitFors(driver, configuredImplicityWait(), by, message);
929     }
930 
931    /**
932     * <p>
933     * Wait for the given amount of seconds, for the given by, using the given driver.  The message is displayed if the
934     * by cannot be found.  No action is performed on the by, so it is possible that the by found is not visible or enabled.
935     * </p>
936     *
937     * @param driver WebDriver to wait on
938     * @param waitSeconds seconds to wait
939     * @param by By to wait for
940     * @param message to display if by is not found in waitSeconds
941     * @return List of WebElements found
942     * @throws InterruptedException
943     */
944     public static List<WebElement> waitFors(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException {
945         // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp
946         //        driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
947         //        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
948 
949         driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS);
950 
951         boolean failed = false;
952 
953         for (int second = 0;; second++) {
954             Thread.sleep(1000);
955             if (second >= waitSeconds) {
956                 failed = true;
957             }
958             try {
959                 if (failed || (driver.findElements(by)).size() > 0) {
960                     break;
961                 }
962             } catch (Exception e) {}
963         }
964 
965         driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS);
966         return driver.findElements(by);  // NOTICE just the find, no action, so by is found, but might not be visible or enabled.
967     }
968 }