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 }