001    /*
002     * Copyright 2005-2013 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     */
016    
017    package edu.samplu.common;
018    
019    import org.apache.commons.lang.StringUtils;
020    import org.openqa.selenium.By;
021    import org.openqa.selenium.NoSuchFrameException;
022    import org.openqa.selenium.Proxy;
023    import org.openqa.selenium.WebDriver;
024    import org.openqa.selenium.chrome.ChromeDriver;
025    import org.openqa.selenium.chrome.ChromeDriverService;
026    import org.openqa.selenium.firefox.FirefoxDriver;
027    import org.openqa.selenium.firefox.FirefoxProfile;
028    import org.openqa.selenium.remote.CapabilityType;
029    import org.openqa.selenium.remote.DesiredCapabilities;
030    import org.openqa.selenium.remote.RemoteWebDriver;
031    import org.openqa.selenium.safari.SafariDriver;
032    
033    import com.thoughtworks.selenium.SeleneseTestBase;
034    
035    import java.io.File;
036    import java.net.MalformedURLException;
037    import java.net.URL;
038    import java.util.concurrent.TimeUnit;
039    
040    
041    /**
042     * The goal of the WebDriverUtil class is to invert the dependencies on WebDriver from WebDriverLegacyITBase for reuse
043     * without having to extend WebDriverLegacyITBase.  For the first example see waitFor
044     *
045     * @see WebDriverLegacyITBase
046     * @author Kuali Rice Team (rice.collab@kuali.org)
047     */
048    public class WebDriverUtil {
049    
050        /**
051         * TODO apparent dup WebDriverITBase.DEFAULT_WAIT_SEC
052         * TODO parametrize for JVM Arg
053         * 30 Seconds
054         */
055        public static int DEFAULT_IMPLICIT_WAIT_TIME = 30;
056    
057        /**
058         * TODO introduce SHORT_IMPLICIT_WAIT_TIME with param in WebDriverITBase
059         * TODO parametrize for JVM Arg
060         * 1 Second
061         */
062        public static int SHORT_IMPLICIT_WAIT_TIME = 1;
063    
064        /**
065         * Set -Dremote.driver.saucelabs for running on saucelabs
066         * @link https://wiki.kuali.org/display/KULRICE/How+To+Run+a+Selenium+Test for patch required
067         */
068        public static final String REMOTE_DRIVER_SAUCELABS_PROPERTY = "remote.driver.saucelabs";
069    
070        /**
071         * Selenium's webdriver.chrome.driver parameter, you can set -Dwebdriver.chrome.driver= or Rice's REMOTE_PUBLIC_CHROME
072         */
073        public static final String WEBDRIVER_CHROME_DRIVER = "webdriver.chrome.driver";
074    
075        /**
076         * Set -Dremote.public.chrome= or WEBDRIVER_CHROME_DRIVER
077         */
078        public static final String REMOTE_PUBLIC_CHROME = "remote.public.chrome";
079    
080        /**
081         * Time to wait for the URL used in setup to load.  Sometimes this is the first hit on the app and it needs a bit
082         * longer than any other.  120 Seconds.
083         * TODO parametrize for JVM Arg
084         */
085        public static final int SETUP_URL_LOAD_WAIT_SECONDS = 120;
086    
087        /**
088         * local proxy used for running tests thru jmeter.
089         * Include host name and port number. Example: localhost:7777
090         */
091        public static final String PROXY_HOST_PROPERTY = "remote.public.proxy";
092    
093        /**
094         * Setup the WebDriver test, login, and load the given web page
095         *
096         * @param username
097         * @param url
098         * @return driver
099         * @throws Exception
100         */
101        public static WebDriver setUp(String username, String url) throws Exception {
102            return setUp(username, url, null, null);
103        }
104    
105        /**
106         * Setup the WebDriver test, login, and load the given web page
107         *
108         * @param username
109         * @param url
110         * @param className
111         * @param testName
112         * @return driver
113         * @throws Exception
114         */
115        public static WebDriver setUp(String username, String url, String className, String testName) throws Exception {
116            WebDriver driver = null;
117            if (System.getProperty(REMOTE_DRIVER_SAUCELABS_PROPERTY) == null) {
118                driver = getWebDriver();
119    //        } else {
120    //            SauceLabsWebDriverHelper saucelabs = new SauceLabsWebDriverHelper();
121    //            saucelabs.setUp(className, testName);
122    //            driver = saucelabs.getDriver();
123            }
124            driver.manage().timeouts().implicitlyWait(SETUP_URL_LOAD_WAIT_SECONDS, TimeUnit.SECONDS);
125    
126            // TODO Got into the situation where the first url doesn't expect server, but all others do.  Readdress once
127            // the NavIT WDIT conversion has been completed.
128            if (!url.startsWith("http")) {
129                url = ITUtil.getBaseUrlString() + url;
130            }
131    
132            driver.get(url);
133            driver.manage().timeouts().implicitlyWait(DEFAULT_IMPLICIT_WAIT_TIME, TimeUnit.SECONDS);
134            return driver;
135        }
136    
137        /**
138         *
139         * @param passed
140         * @param sessionId
141         * @param testParam
142         * @param userParam
143         * @throws Exception
144         */
145        public static void tearDown(boolean passed, String sessionId, String testParam, String userParam) throws Exception {
146    
147    //        if (System.getProperty(SauceLabsWebDriverHelper.SAUCE_PROPERTY) != null) {
148    //            SauceLabsWebDriverHelper.tearDown(passed, sessionId, System.getProperty(SauceLabsWebDriverHelper.SAUCE_USER_PROPERTY),
149    //                    System.getProperty(SauceLabsWebDriverHelper.SAUCE_KEY_PROPERTY));
150    //        }
151    
152            if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) {
153                ITUtil.getHTML(ITUtil.prettyHttp(System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test="
154                        + testParam + "&user=" + userParam));
155            }
156        }
157    
158        /**
159         *
160         * @param testParam
161         * @return
162         */
163        public static String determineUser(String testParam) {
164            String user = null;
165    
166            if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USER_PROPERTY) != null) {
167                return System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USER_PROPERTY);
168            } else if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) { // deprecated
169                String userResponse = ITUtil.getHTML(ITUtil.prettyHttp(System.getProperty(
170                        WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + testParam.trim()));
171                return userResponse.substring(userResponse.lastIndexOf(":") + 2, userResponse.lastIndexOf("\""));
172            }
173    
174            return user;
175        }
176    
177        /***
178         * @link ITUtil#checkForIncidentReport
179         * @param driver
180         * @param locator
181         * @param message
182         */
183        public static void checkForIncidentReport(WebDriver driver, String locator, Failable failable,
184                String message) {
185            ITUtil.checkForIncidentReport(driver.getPageSource(), locator, failable, message);
186        }
187    
188        /**
189         * @link http://code.google.com/p/chromedriver/downloads/list
190         * @link #REMOTE_PUBLIC_CHROME
191         * @link #WEBDRIVER_CHROME_DRIVER
192         * @link ITUtil#HUB_DRIVER_PROPERTY
193         * @return chromeDriverService
194         */
195        public static ChromeDriverService chromeDriverCreateCheck() {
196            String driverParam = System.getProperty(ITUtil.HUB_DRIVER_PROPERTY);
197            // TODO can the saucelabs driver stuff be leveraged here?
198            if (driverParam != null && "chrome".equals(driverParam.toLowerCase())) {
199                if (System.getProperty(WEBDRIVER_CHROME_DRIVER) == null) {
200                    if (System.getProperty(REMOTE_PUBLIC_CHROME) != null) {
201                        System.setProperty(WEBDRIVER_CHROME_DRIVER, System.getProperty(REMOTE_PUBLIC_CHROME));
202                    }
203                }
204                try {
205                    ChromeDriverService chromeDriverService = new ChromeDriverService.Builder()
206                            .usingDriverExecutable(new File(System.getProperty(WEBDRIVER_CHROME_DRIVER)))
207                            .usingAnyFreePort()
208                            .build();
209                    return chromeDriverService;
210                } catch (Throwable t) {
211                    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)   ;
212                }
213            }
214            return null;
215        }
216    
217        /**
218         * remote.public.driver set to chrome or firefox (null assumes firefox)
219         * if remote.public.hub is set a RemoteWebDriver is created (Selenium Grid)
220         * if proxy.host is set, the web driver is setup to use a proxy
221         * @return WebDriver or null if unable to create
222         */
223        public static WebDriver getWebDriver() {
224            String driverParam = System.getProperty(ITUtil.HUB_DRIVER_PROPERTY);
225            String hubParam = System.getProperty(ITUtil.HUB_PROPERTY);
226            String proxyParam = System.getProperty(PROXY_HOST_PROPERTY);
227    
228            // setup proxy if specified as VM Arg
229            DesiredCapabilities capabilities = new DesiredCapabilities();
230            WebDriver webDriver = null;
231            if (StringUtils.isNotEmpty(proxyParam)) {
232                capabilities.setCapability(CapabilityType.PROXY, new Proxy().setHttpProxy(proxyParam));
233            }
234    
235            if (hubParam == null) {
236                if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
237                    FirefoxProfile profile = new FirefoxProfile();
238                    profile.setEnableNativeEvents(false);
239                    capabilities.setCapability(FirefoxDriver.PROFILE, profile);
240                    return new FirefoxDriver(capabilities);
241                } else if ("chrome".equalsIgnoreCase(driverParam)) {
242                    return new ChromeDriver(capabilities);
243                } else if ("safari".equals(driverParam)) {
244                    System.out.println("SafariDriver probably won't work, if it does please contact Erik M.");
245                    return new SafariDriver(capabilities);
246                }
247            } else {
248                try {
249                    if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
250                        return new RemoteWebDriver(new URL(ITUtil.getHubUrlString()), DesiredCapabilities.firefox());
251                    } else if ("chrome".equalsIgnoreCase(driverParam)) {
252                        return new RemoteWebDriver(new URL(ITUtil.getHubUrlString()), DesiredCapabilities.chrome());
253                    }
254                } catch (MalformedURLException mue) {
255                    System.out.println(ITUtil.getHubUrlString() + " " + mue.getMessage());
256                    mue.printStackTrace();
257                }
258            }
259            return null;
260        }
261    
262        /**
263         * If the JVM arg remote.autologin is set, auto login as admin will not be done.
264         * @param driver
265         * @param userName
266         * @param failable
267         * @throws InterruptedException
268         */
269        public static void login(WebDriver driver, String userName, Failable failable) throws InterruptedException {
270            if (System.getProperty(ITUtil.REMOTE_AUTOLOGIN_PROPERTY) == null) {
271                driver.findElement(By.name("__login_user")).clear();
272                driver.findElement(By.name("__login_user")).sendKeys(userName);
273                driver.findElement(By.cssSelector("input[type=\"submit\"]")).click();
274                Thread.sleep(1000);
275                String contents = driver.getPageSource();
276                ITUtil.failOnInvalidUserName(userName, contents, failable);
277            }
278        }
279    
280        protected static void selectFrameSafe(WebDriver driver, String locator) {
281            try {
282                driver.switchTo().frame(locator);
283            } catch (NoSuchFrameException nsfe) {
284                // don't fail
285            }
286        }
287    
288        /**
289         * Wait for the given amount of seconds, for the given by, using the given driver.  The message is displayed if the
290         * by cannot be found.  No action is performed on the by, so it is possible that the by found is not visible or enabled.
291         *
292         * @param driver WebDriver
293         * @param waitSeconds int
294         * @param by By
295         * @param message String
296         * @throws InterruptedException
297         */
298        public static void waitFor(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException {
299            driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
300            Thread.sleep(1000);
301            driver.findElement(by);  // NOTICE just the find, no action, so by is found, but might not be visible or enabled.
302            driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
303        }
304        
305        public static void failOnMatchedJira(String contents, Failable failable) {
306            JiraAwareFailureUtil.failOnMatchedJira(contents, failable);
307        }
308        
309        private static void failWithReportInfoForKim(String contents, String linkLocator, String message) {
310            final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message);
311            SeleneseTestBase.fail(kimIncidentReport);
312        }
313        
314        private static String extractIncidentReportKim(String contents, String linkLocator, String message) {
315            String chunk =  contents.substring(contents.indexOf("id=\"headerarea\""), contents.lastIndexOf("</div>") );
316            String docIdPre = "type=\"hidden\" value=\"";
317            String docId = chunk.substring(chunk.indexOf(docIdPre) + docIdPre.length(), chunk.indexOf("\" name=\"documentId\""));
318    
319            String stackTrace = chunk.substring(chunk.lastIndexOf("name=\"displayMessage\""), chunk.length());
320            String stackTracePre = "value=\"";
321            stackTrace = stackTrace.substring(stackTrace.indexOf(stackTracePre) + stackTracePre.length(), stackTrace.indexOf("name=\"stackTrace\"") - 2);
322    
323            return "\nIncident report "+ message+ " navigating to "+ linkLocator + " Doc Id: "+ docId.trim()+ "\nStackTrace: "+ stackTrace.trim();
324        }
325        
326        private static void processIncidentReport(String contents, String linkLocator, Failable failable, String message) {
327            failOnMatchedJira(contents, failable);
328    
329            if (contents.indexOf("Incident Feedback") > -1) {
330                failWithReportInfo(contents, linkLocator, message);
331            }
332    
333            if (contents.indexOf("Incident Report") > -1) { // KIM incident report
334                failWithReportInfoForKim(contents, linkLocator, message);
335            }
336    
337            SeleneseTestBase.fail("\nIncident report detected " + message + "\n Unable to parse out details for the contents that triggered exception: " + deLinespace(
338                    contents));
339        }
340    
341        private static void failWithReportInfo(String contents, String linkLocator, String message) {
342            final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message);
343            SeleneseTestBase.fail(incidentReportInformation);
344        }
345        
346        private static String extractIncidentReportInfo(String contents, String linkLocator, String message) {
347            String chunk =  contents.substring(contents.indexOf("Incident Feedback"), contents.lastIndexOf("</div>") );
348            String docId = chunk.substring(chunk.lastIndexOf("Document Id"), chunk.indexOf("View Id"));
349            docId = docId.substring(0, docId.indexOf("</span>"));
350            docId = docId.substring(docId.lastIndexOf(">") + 2, docId.length());
351    
352            String viewId = chunk.substring(chunk.lastIndexOf("View Id"), chunk.indexOf("Error Message"));
353            viewId = viewId.substring(0, viewId.indexOf("</span>"));
354            viewId = viewId.substring(viewId.lastIndexOf(">") + 2, viewId.length());
355    
356            String stackTrace = chunk.substring(chunk.lastIndexOf("(only in dev mode)"), chunk.length());
357            stackTrace = stackTrace.substring(stackTrace.indexOf("<span id=\"") + 3, stackTrace.length());
358            stackTrace = stackTrace.substring(stackTrace.indexOf("\">") + 2, stackTrace.indexOf("</span>"));
359        
360            return "\nIncident report "+ message+ " navigating to "+ linkLocator+ " : View Id: "+ viewId.trim()+ " Doc Id: "+ docId.trim()+ "\nStackTrace: "+ stackTrace.trim();
361        }
362        
363        public static String deLinespace(String contents) {
364            while (contents.contains("\n\n")) {
365                contents = contents.replaceAll("\n\n", "\n");
366            }
367            
368            return contents;
369        }
370    }