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 username
267      * @param url
268      * @return driver
269      * @throws Exception
270      */
271     public static WebDriver setUp(String username, String url) throws Exception {
272         return setUp(username, url, null, null);
273     }
274 
275     /**
276      * Setup the WebDriver test, login, and load the given web page
277      *
278      * @param username
279      * @param url
280      * @param className
281      * @param testName
282      * @return driver
283      * @throws Exception
284      */
285     public static WebDriver setUp(String username, String url, String className, String testName) throws Exception {
286         if ("true".equals(System.getProperty(REMOTE_JGROWL_ENABLED, "false"))) {
287             jGrowlEnabled = true;
288         }
289 
290         if ("true".equals(System.getProperty(JS_HIGHLIGHT_PROPERTY, "false"))) {
291             jsHighlightEnabled = true;
292             if (System.getProperty(JS_HIGHLIGHT_INPUT_PROPERTY) != null) {
293                 InputStream in = WebDriverUtils.class.getResourceAsStream(System.getProperty(JS_HIGHLIGHT_INPUT_PROPERTY));
294                 BufferedReader reader = new BufferedReader(new InputStreamReader(in));
295                 String line = null;
296                 List<String> lines = new LinkedList<String>();
297                 while ((line = reader.readLine()) != null) {
298                     lines.add(line);
299                 }
300             }
301         }
302 
303         WebDriver driver = null;
304         if (System.getProperty(SauceLabsWebDriverHelper.REMOTE_DRIVER_SAUCELABS_PROPERTY) == null) {
305             driver = getWebDriver();
306         } else {
307             saucelabs = new SauceLabsWebDriverHelper();
308             saucelabs.setUp(className, testName);
309             driver = saucelabs.getDriver();
310         }
311 
312         driver.manage().timeouts().implicitlyWait(SETUP_URL_LOAD_WAIT_SECONDS, TimeUnit.SECONDS);
313 
314         if (!System.getProperty(SauceLabsWebDriverHelper.SAUCE_BROWSER_PROPERTY,"ff").equals("opera")) {
315             driver.manage().window().maximize();
316 //            driver.manage().window().setSize(new Dimension(800,600));
317         }
318 
319         // TODO Got into the situation where the first url doesn't expect server, but all others do.  Readdress once
320         // the NavIT WDIT conversion has been completed.
321         if (!url.startsWith("http")) {
322             url = getBaseUrlString() + url;
323         }
324 
325         driver.get(url);
326         driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS);
327         return driver;
328     }
329 
330     /**
331      *<p>
332      * Calls {@see SauceLabsWebDriverHelper#tearDown} if {@see #REMOTE_PUBLIC_USERPOOL_PROPERTY} is enabled, calls a user pool
333      * url with the given poolParamTest and poolParamUser.
334      *</p>
335      *
336      * @param passed used by {@see SauceLabsWebDriverHelper#tearDown} to record Saucelabs test status can be null if Saucelabs
337      * is not being used
338      * @param sessionId used by {@see SauceLabsWebDriverHelper#tearDown} to record Saucelabs sessionId status can be null if Saucelabs
339      * is not being used
340      * @param poolParamTest can be null unless a user pool is being used
341      * @param poolParamUser can be null unless a user pool is being used
342      * @throws Exception
343      */
344     public static void tearDown(boolean passed, String sessionId, String poolParamTest, String poolParamUser, String className, String testName) throws Exception {
345 
346         if (passed) {
347             System.out.println("Registering session passed " + sessionId);
348         } else {
349             System.out.println("Registering session failed " + sessionId);
350         }
351 
352         if (System.getProperty(SauceLabsWebDriverHelper.REMOTE_DRIVER_SAUCELABS_PROPERTY) != null) {
353             saucelabs.tearDown(passed, sessionId, className, testName);
354         }
355 
356         if (System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) {
357             AutomatedFunctionalTestUtils.getHTML(AutomatedFunctionalTestUtils.prettyHttp(System.getProperty(
358                     REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + poolParamTest + "&user=" + poolParamUser));
359         }
360     }
361 
362     /**
363      * <p>
364      * If an alert is present accept it print the alert text to System.out
365      * </p>
366      *
367      * @param driver to accept alert on
368      */
369     public static void acceptAlertIfPresent(WebDriver driver) {
370         if (WebDriverUtils.isAlertPresent(driver)) {
371             System.out.println("Alert present " + WebDriverUtils.alertText(driver));
372             alertAccept(driver);
373         }
374     }
375 
376     /**
377      * <p>
378      * Accept the javascript alert (clicking OK).
379      * </p>
380      *
381      * @param driver WebDriver to accept alert on
382      */
383     public static void alertAccept(WebDriver driver) {
384         Alert alert = driver.switchTo().alert();
385         jGrowl(driver, "AFT Step", false, "AFT Step: Accept Alert " + WebDriverUtils.alertText(driver));
386         alert.accept();
387     }
388 
389     /**
390      * <p>
391      * Dismiss the javascript alert (clicking Cancel).
392      * </p>
393      *
394      * @param driver WebDriver to dismiss alert on
395      */
396     public static void alertDismiss(WebDriver driver) {
397         Alert alert = driver.switchTo().alert();
398         jGrowl(driver, "AFT Step", false, "AFT Step: Dismiss Alert " + WebDriverUtils.alertText(driver));
399         alert.dismiss();
400     }
401 
402     /**
403      * <p>
404      * Return alert text.
405      * </p>
406      *
407      * @param driver to get alert text from
408      * @return alert text
409      */
410     public static String alertText(WebDriver driver) {
411         return driver.switchTo().alert().getText();
412     }
413 
414     /**
415      * <p>
416      * <a href="http://code.google.com/p/chromedriver/downloads/list">ChromeDriver downloads</a>, {@see #REMOTE_PUBLIC_CHROME},
417      * {@see #WEBDRIVER_CHROME_DRIVER}, and {@see #HUB_DRIVER_PROPERTY}
418      * </p>
419      *
420      * @return chromeDriverService
421      */
422     public static ChromeDriverService chromeDriverCreateCheck() {
423         String driverParam = System.getProperty(HUB_DRIVER_PROPERTY);
424         // TODO can the saucelabs driver stuff be leveraged here?
425         if (driverParam != null && "chrome".equals(driverParam.toLowerCase())) {
426             if (System.getProperty(WEBDRIVER_CHROME_DRIVER) == null) {
427                 if (System.getProperty(REMOTE_PUBLIC_CHROME) != null) {
428                     System.setProperty(WEBDRIVER_CHROME_DRIVER, System.getProperty(REMOTE_PUBLIC_CHROME));
429                 }
430             }
431             try {
432                 ChromeDriverService chromeDriverService = new ChromeDriverService.Builder()
433                         .usingDriverExecutable(new File(System.getProperty(WEBDRIVER_CHROME_DRIVER)))
434                         .usingAnyFreePort()
435                         .build();
436                 return chromeDriverService;
437             } catch (Throwable t) {
438                 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)   ;
439             }
440         }
441         return null;
442     }
443 
444     /**
445      * <p>
446      * Return the configured implicity wait seconds, {@see #REMOTE_PUBLIC_WAIT_SECONDS_PROPERTY} and {@see #IMPLICIT_WAIT_TIME_SECONDS_DEFAULT}.
447      * </p>
448      *
449      * @return seconds for implicity wait
450      */
451     public static int configuredImplicityWait() {
452         return Integer.parseInt(System.getProperty(REMOTE_PUBLIC_WAIT_SECONDS_PROPERTY, IMPLICIT_WAIT_TIME_SECONDS_DEFAULT + ""));
453     }
454 
455     /**
456      * <p>
457      * Remove double line spacing.
458      * </p>
459      *
460      * @param contents String to remove double line spacing from
461      * @return String with double line spacing removed.
462      */
463     public static String deLinespace(String contents) {
464         while (contents.contains("\n\n")) {
465             contents = contents.replaceAll("\n\n", "\n");
466         }
467 
468         return contents;
469     }
470 
471     /**
472      * <p>
473      * If {@see #REMOTE_PUBLIC_USER_PROPERTY} property is set, return its value, else if {@see #REMOTE_PUBLIC_USERPOOL_PROPERTY}
474      * is set use it to query the userpool service passing the testParam.
475      * </p>
476      * *
477      * @param testParam to use if using a user pool
478      * @return user
479      */
480     public static String determineUser(String testParam) {
481         String user = null;
482 
483         if (System.getProperty(REMOTE_PUBLIC_USER_PROPERTY) != null) {
484             return System.getProperty(REMOTE_PUBLIC_USER_PROPERTY);
485         } else if (System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) { // deprecated
486             String userResponse = AutomatedFunctionalTestUtils.getHTML(AutomatedFunctionalTestUtils.prettyHttp(
487                     System.getProperty(REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + testParam.trim()));
488             return userResponse.substring(userResponse.lastIndexOf(":") + 2, userResponse.lastIndexOf("\""));
489         }
490 
491         return user;
492     }
493 
494     /**
495      * <p>
496      * Setting the JVM arg remote.driver.dontTearDown to y or t leaves the browser window open when the test has completed.
497      * <p></p>
498      * Valuable when debugging, updating, or creating new tests.  When implementing your own tearDown method rather than an
499      * inherited one, it is a common courtesy to include this check and not stop and shutdown the browser window to make it
500      * easy debug or update your test.
501      * </p>
502      *
503      * @return true if the dontTearDownProperty is not set.
504      */
505     public static boolean dontTearDownPropertyNotSet() {
506         return System.getProperty(DONT_TEAR_DOWN_PROPERTY) == null ||
507                 "f".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase()) ||
508                 "n".startsWith(System.getProperty(DONT_TEAR_DOWN_PROPERTY).toLowerCase());
509     }
510 
511     /**
512      * Given the boolean parameter and depending on if {@see #DONT_TEAR_DOWN_ON_FAILURE_PROPERTY} is set to something other
513      * than n, don't tear down the browser window on a test failure.
514 
515      * @param passed
516      * @return
517      */
518     public static boolean dontTearDownOnFailure(boolean passed) {
519         if (!"n".equalsIgnoreCase(System.getProperty(DONT_TEAR_DOWN_ON_FAILURE_PROPERTY, "n"))) {
520             return passed;
521         }
522         return true;
523     }
524 
525     /**
526      * <p>
527      * Find Button by text.
528      * </p>
529      *
530      * @param driver to find button on
531      * @param buttonText text to find button by
532      * @return WebElement of button with button text
533      */
534     public static WebElement findButtonByText(WebDriver driver, String buttonText) {
535         return findElement(driver, By.xpath("//button[contains(text(), '" + buttonText + "')]"));
536     }
537 
538     /**
539      * <p>
540      * Find and highlight the WebElement using the given WebDriver and By.
541      * </p>
542      *
543      * @param driver driver to find on
544      * @param by selector to find
545      * @return
546      */
547     public static WebElement findElement(WebDriver driver, By by) {
548         WebElement found = driver.findElement(by);
549         WebDriverUtils.highlightElement(driver, found);
550         return found;
551     }
552 
553     /**
554      * <p>
555      * In order to run as a smoke test the ability to set the baseUrl via the JVM arg remote.public.url is required.
556      * </p><p>
557      * Trailing slashes are trimmed.  If the remote.public.url does not start with http:// it will be added.
558      * </p>
559      *
560      * @return http://localhost:8080/kr-dev by default else the value of remote.public.url
561      */
562     public static String getBaseUrlString() {
563         String baseUrl = System.getProperty(REMOTE_PUBLIC_URL_PROPERTY);
564         if (baseUrl == null) {
565             baseUrl = DEFAULT_BASE_URL;
566         }
567         baseUrl = AutomatedFunctionalTestUtils.prettyHttp(baseUrl);
568         return baseUrl;
569     }
570 
571     /**
572      * yyyy-MM-dd-HH-mm-ss
573      *
574      * @return Date formatted as yyyy-MM-dd-HH-mm-ss
575      */
576     public static String getDateTimeStampFormatted() {
577         Date now = Calendar.getInstance().getTime();
578         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
579         return sdf.format(now);
580     }
581 
582     /**
583      * <p>
584      * Return the WebElement that has the attribute name with the given value.
585      * </p>
586      *
587      * @param driver to get element from
588      * @param attributeName attribute name to find element by
589      * @param value for the attribute name to find element by
590      * @return WebElement
591      */
592     public static WebElement getElementByAttributeValue(WebDriver driver, String attributeName, String value){
593         return findElement(driver, By.cssSelector("[" + attributeName + "='" + value +"']"));
594     }
595 
596     /**
597      * <p>
598      * 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.
599      * </p><p>
600      * Trailing slashes are trimmed.  If the remote.public.hub does not start with http:// it will be added.
601      * </p>
602      *
603      * @return http://localhost:4444/wd/hub by default else the value of remote.public.hub
604      */
605     public static String getHubUrlString() {
606         String hubUrl = System.getProperty(HUB_PROPERTY);
607         if (hubUrl == null) {
608             hubUrl = HUB_URL_PROPERTY;
609         }
610         hubUrl = AutomatedFunctionalTestUtils.prettyHttp(hubUrl);
611         if (!hubUrl.endsWith("/wd/hub")) {
612             hubUrl = hubUrl + "/wd/hub";
613         }
614         return hubUrl;
615     }
616 
617     /**
618      * <p>
619      * remote.public.driver set to chrome or firefox (null assumes firefox).
620      * </p><p>
621      * if remote.public.hub is set a RemoteWebDriver is created (Selenium Grid)
622      * if proxy.host is set, the web driver is setup to use a proxy
623      * </p>
624      *
625      * @return WebDriver or null if unable to create
626      */
627     public static WebDriver getWebDriver() {
628         String driverParam = System.getProperty(HUB_DRIVER_PROPERTY);
629         String hubParam = System.getProperty(HUB_PROPERTY);
630         String proxyParam = System.getProperty(PROXY_HOST_PROPERTY);
631 
632         // setup proxy if specified as VM Arg
633         DesiredCapabilities capabilities = new DesiredCapabilities();
634         WebDriver webDriver = null;
635         if (StringUtils.isNotEmpty(proxyParam)) {
636             capabilities.setCapability(CapabilityType.PROXY, new Proxy().setHttpProxy(proxyParam));
637         }
638 
639         if (hubParam == null) {
640             if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
641                 FirefoxProfile profile = new FirefoxProfile();
642                 profile.setEnableNativeEvents(false);
643                 capabilities.setCapability(FirefoxDriver.PROFILE, profile);
644                 return new FirefoxDriver(capabilities);
645             } else if ("chrome".equalsIgnoreCase(driverParam)) {
646                 return new ChromeDriver(capabilities);
647             } else if ("safari".equals(driverParam)) {
648                 System.out.println("SafariDriver probably won't work, if it does please contact Erik M.");
649                 return new SafariDriver(capabilities);
650             }
651         } else {
652             try {
653                 if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
654                     return new RemoteWebDriver(new URL(getHubUrlString()), DesiredCapabilities.firefox());
655                 } else if ("chrome".equalsIgnoreCase(driverParam)) {
656                     return new RemoteWebDriver(new URL(getHubUrlString()), DesiredCapabilities.chrome());
657                 }
658             } catch (MalformedURLException mue) {
659                 System.out.println(getHubUrlString() + " " + mue.getMessage());
660                 mue.printStackTrace();
661             }
662         }
663         return null;
664     }
665 
666     public static void highlightElement(WebDriver webDriver, By by) {
667         List<WebElement> elements = webDriver.findElements(by);
668         for (WebElement element : elements) {
669             WebDriverUtils.highlightElement(webDriver, element);
670         }
671     }
672 
673 
674     public static void highlightElements(WebDriver webDriver, List<WebElement> webElements) {
675         for (WebElement webElement: webElements) {
676             highlightElement(webDriver, webElement);
677         }
678     }
679 
680     /**
681      * <p>
682      * Highlight given WebElement.
683      * </p>
684      *
685      * @param webDriver to execute highlight on
686      * @param webElement to highlight
687      */
688     public static void highlightElement(WebDriver webDriver, WebElement webElement) {
689         if (jsHighlightEnabled && webElement != null) {
690             try {
691                 //                System.out.println("highlighting " + webElement.toString() + " on url " + webDriver.getCurrentUrl());
692                 JavascriptExecutor js = (JavascriptExecutor) webDriver;
693                 String jsHighlight = "element = arguments[0];\n"
694                         + "originalStyle = element.getAttribute('style');\n"
695                         + "element.setAttribute('style', originalStyle + \"; background: "
696                         + JS_HIGHLIGHT_BACKGROUND + "; border: 2px solid " + JS_HIGHLIGHT_BOARDER + ";\");\n"
697                         + "setTimeout(function(){\n"
698                         + "    element.setAttribute('style', originalStyle);\n"
699                         + "}, " + System.getProperty(JS_HIGHLIGHT_MS_PROPERTY, JS_HIGHLIGHT_MS + "") + ");";
700                 js.executeScript(jsHighlight, webElement);
701             } catch (Throwable t) {
702                 System.out.println("Throwable during javascript highlight element");
703                 t.printStackTrace();
704             }
705         }
706     }
707 
708     /**
709      * <p>
710      * Return true if an alert is present, false if not.
711      * </p>
712      *
713      * @param driver to check for presents of alert on
714      * @return true if there is an alert present, false if not
715      */
716     public static boolean isAlertPresent(WebDriver driver) {
717         try {
718             driver.switchTo().alert();
719             return true;
720         } catch (Exception e) {
721             return false;
722         }
723     }
724 
725     public static Boolean isTextPresent(WebDriver driver, String pageText, String text) {
726         boolean textPresent = Boolean.FALSE;
727         if (pageText.contains(text)) {
728             WebDriverUtils.highlightElement(driver, By.xpath("//*[contains(text(), '" + text + "')]"));
729             textPresent = Boolean.TRUE;
730         }
731         WebDriverUtils.jGrowl(driver, "Is Text Present?", false, "Is text '" + text + " present?" + " " + textPresent);
732         return textPresent;
733     }
734 
735     /**
736      * <p>
737      * Display jGrowl.
738      * </p>
739      *
740      * @param driver WebDriver to execute jGrowl on
741      * @param jGrowlHeader header text for jGrowl
742      * @param sticky true to set the jGrowl to sticky, false for not sticky
743      * @param message message to display in the jGrowl
744      * @param throwable message and stacktrace to included in jGrowl
745      */
746     public static void jGrowl(WebDriver driver, String jGrowlHeader, boolean sticky, String message, Throwable throwable) {
747         if (jGrowlEnabled) { // check if jGrowl is enabled to skip over the stack trace extraction if it is not.
748             jGrowl(driver, jGrowlHeader, sticky, message + " " + throwable.getMessage() + "\n" + ExceptionUtils.getStackTrace(throwable));
749         }
750     }
751 
752     /**
753      * <p>
754      * Display jGrowl.
755      * </p>
756      *
757      * @param driver WebDriver to execute jGrowl on
758      * @param jGrowlHeader header text for jGrowl
759      * @param sticky true to set the jGrowl to sticky, false for not sticky
760      * @param message message to display in the jGrowl
761      */
762     public static void jGrowl(WebDriver driver, String jGrowlHeader, boolean sticky, String message) {
763         stepMessage(message);
764         if (jGrowlEnabled) {
765             try {
766                 String javascript="jQuery.jGrowl('" + message + "' , {sticky: " + sticky + ", header : '" + jGrowlHeader + "'});";
767                 ((JavascriptExecutor) driver).executeScript(javascript);
768             } catch (Throwable t) {
769                 jGrowlException(t);
770             }
771         }
772     }
773 
774     /**
775      * <p>
776      * Print jGrowl Exception to System.out, if {@see #JGROWL_ERROR_FAILURE} is set to true, fail.
777      * </p>
778      *
779      * @param throwable message and stack trace to print and if configured fail with
780      */
781     public static void jGrowlException(Throwable throwable) {
782         String failMessage = throwable.getMessage() + "\n" + ExceptionUtils.getStackTrace(throwable);
783         System.out.println("jGrowl failure " + failMessage);
784         if (JGROWL_ERROR_FAILURE) {
785             SeleneseTestBase.fail(failMessage); // SeleneseTestBase fail okay here as jGrowl failures are not Jira worthy yet
786         }
787     }
788 
789     /**
790      * <p>
791      * Select frame defined by locator without throwing an Exception if it doesn't exist.
792      * </p>
793      *
794      * @param driver to select frame on
795      * @param locator to identify frame to select
796      */
797     public static void selectFrameSafe(WebDriver driver, String locator) {
798         try {
799             driver.switchTo().frame(locator);
800         } catch (NoSuchFrameException nsfe) {
801             // don't fail
802         }
803     }
804 
805     public static void stepMessage(String message) {
806         System.out.println("AFT Step: " + message);
807     }
808 
809     /**
810      * <p>
811      * Return the WebElement that has the attribute name with the given value within the given seconds to wait.
812      * </p>
813      *
814      * @param driver to get element from
815      * @param attribute name to find element by
816      * @param attributeValue for the attribute name to find element by
817      * @param waitSeconds number of seconds to wait
818      * @return WebElement
819      * @throws InterruptedException
820      */
821     public static WebElement waitAndGetElementByAttributeValue(WebDriver driver, String attribute, String attributeValue, int waitSeconds) throws InterruptedException {
822         // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp
823         //        driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
824         //        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
825 
826         driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
827 
828         boolean failed = false;
829 
830         for (int second = 0;; second++) {
831             Thread.sleep(1000);
832             if (second >= waitSeconds) {
833                 failed = true;
834             }
835             try {
836                 if (failed || (getElementByAttributeValue(driver, attribute, attributeValue) != null)) {
837                     break;
838                 }
839             } catch (Exception e) {}
840         }
841 
842         WebElement element = getElementByAttributeValue(driver, attribute, attributeValue);
843         driver.manage().timeouts().implicitlyWait(WebDriverUtils.configuredImplicityWait(), TimeUnit.SECONDS);
844         return element;
845     }
846 
847     public static void waitToAcceptAlert(WebDriver driver, int waitSeconds, String message) throws InterruptedException {
848         driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS);
849 
850         boolean failed = false;
851 
852         for (int second = 0;; second++) {
853             Thread.sleep(1000);
854             if (second >= waitSeconds) {
855                 failed = true;
856             }
857             try {
858                 if (failed) {
859                     break;
860                 } else if (isAlertPresent(driver)) {
861                     acceptAlertIfPresent(driver);
862                     break;
863                 }
864             } catch (Exception e) {}
865         }
866 
867         driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS);
868     }
869 
870     /**
871      * <p>
872      * Wait for the given amount of seconds, for the given by, using the given driver.  The message is displayed if the
873      * by cannot be found.  No action is performed on the by, so it is possible that the by found is not visible or enabled.
874      * </p>
875      *
876      * @param driver WebDriver to wait on
877      * @param waitSeconds seconds to wait
878      * @param by By to wait for
879      * @param message to display if by is not found in waitSeconds
880      * @throws InterruptedException
881      */
882     public static WebElement waitFor(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException {
883         // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp
884         //        driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
885         //        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
886 
887         driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS);
888 
889         boolean failed = false;
890         WebElement element = null;
891 
892         for (int second = 0;; second++) {
893             Thread.sleep(1000);
894             if (second >= waitSeconds) {
895                 failed = true;
896             }
897             try {
898                 if (failed) {
899                     break;
900                 } else if ((driver.findElements(by)).size() > 0) {
901                     element = findElement(driver, by);  // NOTICE just the find, no action, so by is found, but might not be visible or enabled.
902                     highlightElement(driver, element);
903                     break;
904                 }
905             } catch (Exception e) {}
906         }
907 
908         driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS);
909         return element;
910     }
911 
912     /**
913      * <p>
914      * Wait for WebElements.
915      * </p>
916      *
917      * @param driver WebDriver to wait on
918      * @param by By to wait for
919      * @return
920      * @throws InterruptedException
921      */
922     public static List<WebElement> waitFors(WebDriver driver, By by) throws InterruptedException {
923         return waitFors(driver, configuredImplicityWait(), by, "");
924     }
925 
926     /**
927      * <p>
928      * Wait for WebElements.
929      * </p>
930      *
931      * @param driver WebDriver to wait on
932      * @param by By to wait for
933      * @param message to display if by is not found in waitSeconds
934      * @return List of WebElements found
935      * @throws InterruptedException
936      */
937     public static List<WebElement> waitFors(WebDriver driver, By by, String message) throws InterruptedException {
938         return waitFors(driver, configuredImplicityWait(), by, message);
939     }
940 
941    /**
942     * <p>
943     * Wait for the given amount of seconds, for the given by, using the given driver.  The message is displayed if the
944     * by cannot be found.  No action is performed on the by, so it is possible that the by found is not visible or enabled.
945     * </p>
946     *
947     * @param driver WebDriver to wait on
948     * @param waitSeconds seconds to wait
949     * @param by By to wait for
950     * @param message to display if by is not found in waitSeconds
951     * @return List of WebElements found
952     * @throws InterruptedException
953     */
954     public static List<WebElement> waitFors(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException {
955         // jenkins implies that implicitlyWait is worse than sleep loop for finding elements by 100+ test failures on the old sampleapp
956         //        driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
957         //        driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
958 
959         driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_TIME_LOOP_MS, TimeUnit.MILLISECONDS);
960 
961         boolean failed = false;
962 
963         for (int second = 0;; second++) {
964             Thread.sleep(1000);
965             if (second >= waitSeconds) {
966                 failed = true;
967             }
968             try {
969                 if (failed || (driver.findElements(by)).size() > 0) {
970                     break;
971                 }
972             } catch (Exception e) {}
973         }
974 
975         driver.manage().timeouts().implicitlyWait(configuredImplicityWait(), TimeUnit.SECONDS);
976         return driver.findElements(by);  // NOTICE just the find, no action, so by is found, but might not be visible or enabled.
977     }
978 }