View Javadoc

1   /*
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package edu.samplu.common;
18  
19  import org.junit.rules.TestName;
20  import org.openqa.selenium.By;
21  import org.openqa.selenium.NoSuchFrameException;
22  import org.openqa.selenium.WebDriver;
23  import org.openqa.selenium.chrome.ChromeDriver;
24  import org.openqa.selenium.chrome.ChromeDriverService;
25  import org.openqa.selenium.firefox.FirefoxDriver;
26  import org.openqa.selenium.firefox.FirefoxProfile;
27  import org.openqa.selenium.remote.DesiredCapabilities;
28  import org.openqa.selenium.remote.RemoteWebDriver;
29  import org.openqa.selenium.safari.SafariDriver;
30  
31  import com.thoughtworks.selenium.SeleneseTestBase;
32  
33  import java.io.File;
34  import java.net.MalformedURLException;
35  import java.net.URL;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.Map;
39  import java.util.concurrent.TimeUnit;
40  
41  
42  /**
43   * The goal of the WebDriverUtil class is to invert the dependencies on WebDriver from WebDriverLegacyITBase for reuse
44   * without having to extend WebDriverLegacyITBase.  For the first example see waitFor
45   *
46   * @see WebDriverLegacyITBase
47   * @author Kuali Rice Team (rice.collab@kuali.org)
48   */
49  public class WebDriverUtil {
50  
51      /**
52       * TODO apparent dup WebDriverITBase.DEFAULT_WAIT_SEC
53       * TODO parametrize for JVM Arg
54       * 30 Seconds
55       */
56      public static int DEFAULT_IMPLICIT_WAIT_TIME = 30;
57  
58      /**
59       * TODO introduce SHORT_IMPLICIT_WAIT_TIME with param in WebDriverITBase
60       * TODO parametrize for JVM Arg
61       * 1 Second
62       */
63      public static int SHORT_IMPLICIT_WAIT_TIME = 1;
64  
65      /**
66       * Set -Dremote.driver.saucelabs for running on saucelabs
67       * @link https://wiki.kuali.org/display/KULRICE/How+To+Run+a+Selenium+Test for patch required
68       */
69      public static final String REMOTE_DRIVER_SAUCELABS_PROPERTY = "remote.driver.saucelabs";
70  
71      /**
72       * Selenium's webdriver.chrome.driver parameter, you can set -Dwebdriver.chrome.driver= or Rice's REMOTE_PUBLIC_CHROME
73       */
74      public static final String WEBDRIVER_CHROME_DRIVER = "webdriver.chrome.driver";
75  
76      /**
77       * Set -Dremote.public.chrome= or WEBDRIVER_CHROME_DRIVER
78       */
79      public static final String REMOTE_PUBLIC_CHROME = "remote.public.chrome";
80  
81      /**
82       * 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
83       * longer than any other.  120 Seconds.
84       * TODO parametrize for JVM Arg
85       */
86      public static final int SETUP_URL_LOAD_WAIT_SECONDS = 120;
87      
88      /**
89       * https://jira.kuali.org/browse/
90       */
91      public static final String JIRA_BROWSE_URL = "https://jira.kuali.org/browse/";
92      static Map<String, String> jiraMatches;
93      static {
94          jiraMatches = new HashMap<String, String>();
95          jiraMatches.put("Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'refreshWhenChanged' of bean class [org.kuali.rice.krad.uif.element.Action]: Bean property 'refreshWhenChanged' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?",
96                  "KULRICE-8137 Agenda Rule edit Incident report Invalid property 'refreshWhenChanged'");
97  
98          jiraMatches.put("org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase.processAddCollectionLineBusinessRules(MaintenanceDocumentRuleBase.",
99                  "KULRICE-8142 NPE in MaintenanceDocumentRuleBase.processAddCollectionLineBusinessRules");
100 
101         jiraMatches.put("at org.kuali.rice.krad.rules.DocumentRuleBase.isDocumentOverviewValid(DocumentRuleBase.",
102                 "KULRICE-8134 NPE in DocumentRuleBase.isDocumentOverviewValid(DocumentRuleBase");
103 
104         jiraMatches.put("org.kuali.rice.krad.uif.layout.TableLayoutManager.buildLine(TableLayoutManager.",
105                 "KULRICE-8160 NPE at TableLayoutManager.buildLine(TableLayoutManager");
106 
107         jiraMatches.put("Bean property 'configFileLocations' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?",
108                 "KULRICE-8173 Bean property 'configFileLocations' is not writable or has an invalid setter method");
109 
110         jiraMatches.put("Bean property 'componentSecurity' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?",
111                 "KULRICE-8182 JDK7 Bean property 'componentSecurity' is not readable...");
112 
113         jiraMatches.put("java.sql.SQLSyntaxErrorException: ORA-00904: \"ROUTEHEADERID\": invalid identifier",
114                 "KULRICE-8277 Several ITs fail with OJB operation; bad SQL grammar []; nested exception is java.sql.SQLException: ORA-00904: \"ROUTEHEADERID\": invalid identifier");
115 
116         jiraMatches.put("By.xpath: //button[@data-loadingmessage='Adding Line...']",
117                 "KULRICE-9044 KRAD \"stacked\" collection elements are not rendering add/delete buttons ");
118 
119         jiraMatches.put("Error: on line 135, column 39 in krad/WEB-INF/ftl/lib/grid.ftl",
120                 "KULRICE-9047 Term maintenance freemarker exception ");
121     }
122     
123     /**
124      * Setup the WebDriver test, login, and load the given web page
125      *
126      * @param username
127      * @param url
128      * @return driver
129      * @throws Exception
130      */
131     public static WebDriver setUp(String username, String url) throws Exception {
132         return setUp(username, url, null, null);
133     }
134 
135     /**
136      * Setup the WebDriver test, login, and load the given web page
137      *
138      * @param username
139      * @param url
140      * @param className
141      * @param testName
142      * @return driver
143      * @throws Exception
144      */
145     public static WebDriver setUp(String username, String url, String className, TestName testName) throws Exception {
146         WebDriver driver = null;
147         if (System.getProperty(REMOTE_DRIVER_SAUCELABS_PROPERTY) == null) {
148             driver = getWebDriver();
149 //        } else {
150 //            SauceLabsWebDriverHelper saucelabs = new SauceLabsWebDriverHelper();
151 //            saucelabs.setUp(className, testName);
152 //            driver = saucelabs.getDriver();
153         }
154         driver.manage().timeouts().implicitlyWait(SETUP_URL_LOAD_WAIT_SECONDS, TimeUnit.SECONDS);
155 
156         // TODO Got into the situation where the first url doesn't expect server, but all others do.  Readdress once
157         // the NavIT WDIT conversion has been completed.
158         if (!url.startsWith("http")) {
159             url = ITUtil.getBaseUrlString() + url;
160         }
161 
162         driver.get(url);
163         driver.manage().timeouts().implicitlyWait(DEFAULT_IMPLICIT_WAIT_TIME, TimeUnit.SECONDS);
164         return driver;
165     }
166 
167     /**
168      *
169      * @param passed
170      * @param sessionId
171      * @param testParam
172      * @param userParam
173      * @throws Exception
174      */
175     public static void tearDown(boolean passed, String sessionId, String testParam, String userParam) throws Exception {
176 
177 //        if (System.getProperty(SauceLabsWebDriverHelper.SAUCE_PROPERTY) != null) {
178 //            SauceLabsWebDriverHelper.tearDown(passed, sessionId, System.getProperty(SauceLabsWebDriverHelper.SAUCE_USER_PROPERTY),
179 //                    System.getProperty(SauceLabsWebDriverHelper.SAUCE_KEY_PROPERTY));
180 //        }
181 
182         if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) {
183             ITUtil.getHTML(ITUtil.prettyHttp(System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test="
184                     + testParam + "&user=" + userParam));
185         }
186     }
187 
188     /**
189      *
190      * @param testParam
191      * @return
192      */
193     public static String determineUser(String testParam) {
194         String user = null;
195 
196         if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USER_PROPERTY) != null) {
197             return System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USER_PROPERTY);
198         } else if (System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) != null) { // deprecated
199             String userResponse = ITUtil.getHTML(ITUtil.prettyHttp(System.getProperty(
200                     WebDriverLegacyITBase.REMOTE_PUBLIC_USERPOOL_PROPERTY) + "?test=" + testParam.trim()));
201             return userResponse.substring(userResponse.lastIndexOf(":") + 2, userResponse.lastIndexOf("\""));
202         }
203 
204         return user;
205     }
206 
207     /***
208      * @link ITUtil#checkForIncidentReport
209      * @param driver
210      * @param locator
211      * @param message
212      */
213     public static void checkForIncidentReport(WebDriver driver, String locator, Failable failable,
214             String message) {
215         ITUtil.checkForIncidentReport(driver.getPageSource(), locator, failable, message);
216     }
217 
218     /**
219      * @link http://code.google.com/p/chromedriver/downloads/list
220      * @link #REMOTE_PUBLIC_CHROME
221      * @link #WEBDRIVER_CHROME_DRIVER
222      * @link ITUtil#HUB_DRIVER_PROPERTY
223      * @return chromeDriverService
224      */
225     public static ChromeDriverService chromeDriverCreateCheck() {
226         String driverParam = System.getProperty(ITUtil.HUB_DRIVER_PROPERTY);
227         // TODO can the saucelabs driver stuff be leveraged here?
228         if (driverParam != null && "chrome".equals(driverParam.toLowerCase())) {
229             if (System.getProperty(WEBDRIVER_CHROME_DRIVER) == null) {
230                 if (System.getProperty(REMOTE_PUBLIC_CHROME) != null) {
231                     System.setProperty(WEBDRIVER_CHROME_DRIVER, System.getProperty(REMOTE_PUBLIC_CHROME));
232                 }
233             }
234             try {
235                 ChromeDriverService chromeDriverService = new ChromeDriverService.Builder()
236                         .usingChromeDriverExecutable(new File(System.getProperty(WEBDRIVER_CHROME_DRIVER)))
237                         .usingAnyFreePort()
238                         .build();
239                 return chromeDriverService;
240             } catch (Throwable t) {
241                 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)   ;
242             }
243         }
244         return null;
245     }
246 
247     /**
248      * remote.public.driver set to chrome or firefox (null assumes firefox)
249      * if remote.public.hub is set a RemoteWebDriver is created (Selenium Grid)
250      * @return WebDriver or null if unable to create
251      */
252     public static WebDriver getWebDriver() {
253         String driverParam = System.getProperty(ITUtil.HUB_DRIVER_PROPERTY);
254         String hubParam = System.getProperty(ITUtil.HUB_PROPERTY);
255         if (hubParam == null) {
256             if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
257                 FirefoxProfile profile = new FirefoxProfile();
258                 profile.setEnableNativeEvents(false);
259                 return new FirefoxDriver(profile);
260             } else if ("chrome".equalsIgnoreCase(driverParam)) {
261                 return new ChromeDriver();
262             } else if ("safari".equals(driverParam)) {
263                 System.out.println("SafariDriver probably won't work, if it does please contact Erik M.");
264                 return new SafariDriver();
265             }
266         } else {
267             try {
268                 if (driverParam == null || "firefox".equalsIgnoreCase(driverParam)) {
269                     return new RemoteWebDriver(new URL(ITUtil.getHubUrlString()), DesiredCapabilities.firefox());
270                 } else if ("chrome".equalsIgnoreCase(driverParam)) {
271                     return new RemoteWebDriver(new URL(ITUtil.getHubUrlString()), DesiredCapabilities.chrome());
272                 }
273             } catch (MalformedURLException mue) {
274                 System.out.println(ITUtil.getHubUrlString() + " " + mue.getMessage());
275                 mue.printStackTrace();
276             }
277         }
278         return null;
279     }
280 
281     /**
282      * If the JVM arg remote.autologin is set, auto login as admin will not be done.
283      * @param driver
284      * @param userName
285      * @param failable
286      * @throws InterruptedException
287      */
288     public static void login(WebDriver driver, String userName, Failable failable) throws InterruptedException {
289         if (System.getProperty(ITUtil.REMOTE_AUTOLOGIN_PROPERTY) == null) {
290             driver.findElement(By.name("__login_user")).clear();
291             driver.findElement(By.name("__login_user")).sendKeys(userName);
292             driver.findElement(By.cssSelector("input[type=\"submit\"]")).click();
293             Thread.sleep(1000);
294             String contents = driver.getPageSource();
295             ITUtil.failOnInvalidUserName(userName, contents, failable);
296         }
297     }
298 
299     protected static void selectFrameSafe(WebDriver driver, String locator) {
300         try {
301             driver.switchTo().frame(locator);
302         } catch (NoSuchFrameException nsfe) {
303             // don't fail
304         }
305     }
306 
307     /**
308      * Wait for the given amount of seconds, for the given by, using the given driver.  The message is displayed if the
309      * by cannot be found.  No action is performed on the by, so it is possible that the by found is not visible or enabled.
310      *
311      * @param driver WebDriver
312      * @param waitSeconds int
313      * @param by By
314      * @param message String
315      * @throws InterruptedException
316      */
317     public static void waitFor(WebDriver driver, int waitSeconds, By by, String message) throws InterruptedException {
318         driver.manage().timeouts().implicitlyWait(waitSeconds, TimeUnit.SECONDS);
319         Thread.sleep(1000);
320         driver.findElement(by);  // NOTICE just the find, no action, so by is found, but might not be visible or enabled.
321         driver.manage().timeouts().implicitlyWait(1, TimeUnit.SECONDS);
322     }
323     
324     public static void failOnMatchedJira(String contents) {
325         Iterator<String> iter = jiraMatches.keySet().iterator();
326         String key = null;
327 
328         while (iter.hasNext()) {
329             key = iter.next();
330             if (contents.contains(key)) {
331                 SeleneseTestBase.fail(JIRA_BROWSE_URL + jiraMatches.get(key));
332             }
333         }
334     }
335     
336     private static void failWithReportInfoForKim(String contents, String linkLocator, String message) {
337         final String kimIncidentReport = extractIncidentReportKim(contents, linkLocator, message);
338         SeleneseTestBase.fail(kimIncidentReport);
339     }
340     
341     private static String extractIncidentReportKim(String contents, String linkLocator, String message) {
342         String chunk =  contents.substring(contents.indexOf("id=\"headerarea\""), contents.lastIndexOf("</div>") );
343         String docIdPre = "type=\"hidden\" value=\"";
344         String docId = chunk.substring(chunk.indexOf(docIdPre) + docIdPre.length(), chunk.indexOf("\" name=\"documentId\""));
345 
346         String stackTrace = chunk.substring(chunk.lastIndexOf("name=\"displayMessage\""), chunk.length());
347         String stackTracePre = "value=\"";
348         stackTrace = stackTrace.substring(stackTrace.indexOf(stackTracePre) + stackTracePre.length(), stackTrace.indexOf("name=\"stackTrace\"") - 2);
349 
350         return "\nIncident report "+ message+ " navigating to "+ linkLocator + " Doc Id: "+ docId.trim()+ "\nStackTrace: "+ stackTrace.trim();
351     }
352     
353     private static void processIncidentReport(String contents, String linkLocator, String message) {
354         failOnMatchedJira(contents);
355 
356         if (contents.indexOf("Incident Feedback") > -1) {
357             failWithReportInfo(contents, linkLocator, message);
358         }
359 
360         if (contents.indexOf("Incident Report") > -1) { // KIM incident report
361             failWithReportInfoForKim(contents, linkLocator, message);
362         }
363 
364         SeleneseTestBase.fail("\nIncident report detected " + message + "\n Unable to parse out details for the contents that triggered exception: " + deLinespace(
365                 contents));
366     }
367 
368     private static void failWithReportInfo(String contents, String linkLocator, String message) {
369         final String incidentReportInformation = extractIncidentReportInfo(contents, linkLocator, message);
370         SeleneseTestBase.fail(incidentReportInformation);
371     }
372     
373     private static String extractIncidentReportInfo(String contents, String linkLocator, String message) {
374         String chunk =  contents.substring(contents.indexOf("Incident Feedback"), contents.lastIndexOf("</div>") );
375         String docId = chunk.substring(chunk.lastIndexOf("Document Id"), chunk.indexOf("View Id"));
376         docId = docId.substring(0, docId.indexOf("</span>"));
377         docId = docId.substring(docId.lastIndexOf(">") + 2, docId.length());
378 
379         String viewId = chunk.substring(chunk.lastIndexOf("View Id"), chunk.indexOf("Error Message"));
380         viewId = viewId.substring(0, viewId.indexOf("</span>"));
381         viewId = viewId.substring(viewId.lastIndexOf(">") + 2, viewId.length());
382 
383         String stackTrace = chunk.substring(chunk.lastIndexOf("(only in dev mode)"), chunk.length());
384         stackTrace = stackTrace.substring(stackTrace.indexOf("<span id=\"") + 3, stackTrace.length());
385         stackTrace = stackTrace.substring(stackTrace.indexOf("\">") + 2, stackTrace.indexOf("</span>"));
386     
387         return "\nIncident report "+ message+ " navigating to "+ linkLocator+ " : View Id: "+ viewId.trim()+ " Doc Id: "+ docId.trim()+ "\nStackTrace: "+ stackTrace.trim();
388     }
389     
390     public static String deLinespace(String contents) {
391         while (contents.contains("\n\n")) {
392             contents = contents.replaceAll("\n\n", "\n");
393         }
394         
395         return contents;
396     }
397 }