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