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 package edu.samplu.common; 017 018 import com.saucelabs.common.SauceOnDemandAuthentication; 019 import com.saucelabs.common.SauceOnDemandSessionIdProvider; 020 import com.saucelabs.junit.SauceOnDemandTestWatcher; 021 import com.saucelabs.saucerest.SauceREST; 022 import org.junit.Assert; 023 import org.openqa.selenium.Platform; 024 import org.openqa.selenium.WebDriver; 025 import org.openqa.selenium.ie.InternetExplorerDriver; 026 import org.openqa.selenium.remote.DesiredCapabilities; 027 import org.openqa.selenium.remote.RemoteWebDriver; 028 029 import java.io.BufferedWriter; 030 import java.io.File; 031 import java.io.FileWriter; 032 import java.io.IOException; 033 import java.net.URL; 034 import java.util.HashMap; 035 import java.util.Map; 036 037 /** 038 * Simple {@link org.openqa.selenium.remote.RemoteWebDriver} test that demonstrates how to run your Selenium tests with <a href="http://saucelabs.com/ondemand">Sauce OnDemand</a>. 039 * 040 * This test also includes the <a href="">Sauce JUnit</a> helper classes, which will use the Sauce REST API to mark the Sauce Job as passed/failed. 041 * 042 * In order to use the {@link SauceOnDemandTestWatcher}, the test must implement the {@link SauceOnDemandSessionIdProvider} interface. 043 * 044 */ 045 public class SauceLabsWebDriverHelper implements SauceOnDemandSessionIdProvider { 046 047 /** 048 * remote.driver.saucelabs.share 049 */ 050 public static final String SAUCE_SHARE_PROPERTY = "remote.driver.saucelabs.share"; 051 052 /** 053 * remote.driver.saucelabs.pop.disable 054 */ 055 public static final String SAUCE_POPUP_PROPERTY = "remote.driver.saucelabs.pop.disable"; 056 057 /** 058 * remote.driver.saucelabs 059 */ 060 public static final String SAUCE_PROPERTY = "remote.driver.saucelabs"; 061 062 /** 063 * remote.driver.saucelabs.platform 064 */ 065 public static final String SAUCE_PLATFORM_PROPERTY = "remote.driver.saucelabs.platform"; 066 067 /** 068 * remote.driver.saucelabs.browser 069 */ 070 public static final String SAUCE_BROWSER_PROPERTY = "remote.driver.saucelabs.browser"; 071 072 /** 073 * remote.driver.saucelabs.user 074 */ 075 public static final String SAUCE_USER_PROPERTY = "remote.driver.saucelabs.user"; 076 077 /** 078 * remote.driver.saucelabs.key 079 */ 080 public static final String SAUCE_KEY_PROPERTY = "remote.driver.saucelabs.key"; 081 082 /** 083 * remote.driver.saucelabs.version 084 */ 085 public static final String SAUCE_VERSION_PROPERTY = "remote.driver.saucelabs.version"; 086 087 /** 088 * Constructs a {@link SauceOnDemandAuthentication} instance using the supplied user name/access key. To use the authentication 089 * supplied by environment variables or from an external file, use the no-arg {@link SauceOnDemandAuthentication} constructor. 090 */ 091 public SauceOnDemandAuthentication authentication = new SauceOnDemandAuthentication(System.getProperty(SAUCE_USER_PROPERTY), System.getProperty(SAUCE_KEY_PROPERTY)); 092 093 private WebDriver driver; 094 095 private String sessionId; 096 097 /** 098 * Saucelabs setup 099 * @param className 100 * @param testName 101 * @throws Exception 102 */ 103 public void setUp(String className, String testName) throws Exception { 104 if (System.getProperty(SAUCE_USER_PROPERTY) == null || System.getProperty(SAUCE_KEY_PROPERTY) == null) { 105 Assert.fail("-D" + SAUCE_USER_PROPERTY + " and -D" + SAUCE_KEY_PROPERTY + " must be set to saucelabs user and access key."); 106 } 107 108 DesiredCapabilities capabilities = null; 109 if ("ff".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 110 capabilities = DesiredCapabilities.firefox(); 111 } else if ("ie".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 112 capabilities = DesiredCapabilities.internetExplorer(); 113 capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,true); 114 } else if ("chrome".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 115 capabilities = DesiredCapabilities.chrome(); 116 } else if ("opera".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 117 capabilities = DesiredCapabilities.opera(); 118 } else if ("android".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 119 capabilities = DesiredCapabilities.android(); 120 } else if ("safari".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 121 capabilities = DesiredCapabilities.safari(); 122 } else if ("ipad".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 123 capabilities = DesiredCapabilities.ipad(); 124 } else if ("iphone".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 125 capabilities = DesiredCapabilities.iphone(); 126 } else { 127 capabilities = DesiredCapabilities.firefox(); 128 } 129 130 String version = System.getProperty(SAUCE_VERSION_PROPERTY); 131 if (version == null || "0".equals(version)) { // Blank or 0 leaves version blank for use with chrome 132 133 if (!"chrome".equalsIgnoreCase(System.getProperty(SAUCE_BROWSER_PROPERTY))) { 134 throw new RuntimeException("Blank or 0 version for a browser not chrome " + System.getProperty(SAUCE_BROWSER_PROPERTY)); 135 } 136 137 capabilities.setCapability("version", ""); // saucelabs requires blank for chrome (latest version) 138 } else { 139 capabilities.setCapability("version", version); // saucelabs requires blank for chrome (latest version) 140 } 141 142 capabilities.setCapability("platform", System.getProperty(SAUCE_PLATFORM_PROPERTY, Platform.UNIX.toString()).replaceAll("_", " ")); 143 capabilities.setCapability("idle-timeout", 180); 144 capabilities.setCapability("max-duration", 480); 145 capabilities.setCapability("name", className + "." + testName + "-" + ITUtil.DTS); 146 capabilities.setCapability("disable-popup-handler", System.getProperty(SAUCE_POPUP_PROPERTY, "false")); 147 capabilities.setCapability("public", System.getProperty(SAUCE_SHARE_PROPERTY, "share")); 148 149 this.driver = new RemoteWebDriver( 150 new URL("http://" + authentication.getUsername() + ":" + authentication.getAccessKey() + "@ondemand.saucelabs.com:80/wd/hub"), 151 capabilities); 152 this.sessionId = ((RemoteWebDriver)driver).getSessionId().toString(); 153 154 // TODO it would be better to do these at tear down, passing state could then be included in names, but requires more parameters 155 try { 156 String dir = determineSaveDir(className, testName); 157 String resources = "mkdir " + dir + " ; cd " + dir + " ; \n" 158 + curlSaveResourceString(className, testName, "selenium-server.log") + " ; \n" 159 + curlSaveResourceString(className, testName, "video.flv") + " ; \n" 160 + wgetnSaveResourceString(className, testName) + " ; \n" 161 + "cd ../\n"; 162 System.out.println(resources); 163 writeFile("SauceLabsResources" + dir + ".sh", resources); 164 } catch (Exception e) { 165 System.out.println("Exception while writing SauceLabsResources.sh " + e.getMessage()); 166 System.out.println(curlSaveResourceString(className, testName, "selenium-server.log")); 167 System.out.println(curlSaveResourceString(className, testName, "video.flv")); 168 // System.out.println(curlSaveResourceString(className, testName, "XXXXscreenshot.png (where XXXX is a number between 0000 and 9999)")); // TODO 169 } 170 } 171 172 /** 173 * Do Suacelabs related teardown things. Mostly flag the tests as passed or failed. 174 * @param passed 175 * @param sessionId 176 * @param sauceUser 177 * @param sauceKey 178 * @throws Exception 179 */ 180 public static void tearDown(boolean passed, String sessionId, String sauceUser, String sauceKey) throws Exception { 181 if (sessionId != null && System.getProperty(SAUCE_PROPERTY) != null) { 182 SauceREST client = new SauceREST(sauceUser, sauceKey); 183 /* Using a map of udpates: 184 * (http://saucelabs.com/docs/sauce-ondemand#alternative-annotation-methods) 185 */ 186 Map<String, Object> updates = new HashMap<String, Object>(); 187 updates.put("passed", passed); 188 updates.put("build", System.getProperty("rice.version", "unknown")); 189 client.updateJobInfo(sessionId, updates); 190 191 if (passed) { 192 System.out.println("Registering session passed " + sessionId); 193 client.jobPassed(sessionId); 194 } else { 195 System.out.println("Registering session failed " + sessionId); 196 client.jobFailed(sessionId); 197 } 198 199 Thread.sleep(5000); // give the client message a chance to get processed on saucelabs side 200 } 201 } 202 203 private String curlSaveResourceString(String className, String testName, String resource) { 204 return "curl -o " + deriveResourceBaseNames(className, testName, resource) 205 + " -u " + authentication.getUsername() + ":" + authentication.getAccessKey() 206 + " http://saucelabs.com/rest/" + authentication.getUsername()+ "/jobs/" + sessionId + "/results/" + resource; 207 } 208 209 private String deriveResourceBaseNames(String className, String testName, String resource) { 210 return className + "." + testName + "-" 211 + System.getProperty(SAUCE_PLATFORM_PROPERTY, Platform.UNIX.toString()) + "-" 212 + System.getProperty(SAUCE_BROWSER_PROPERTY) + "-" 213 + System.getProperty(SAUCE_VERSION_PROPERTY) + "-" 214 + System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USER_PROPERTY, "admin") + "-" 215 + System.getProperty("rice.version", "unknown_build") + "-" 216 + ITUtil.DTS + "-" 217 + resource; 218 } 219 220 /** 221 * Returns the driver 222 * @return WebDriver 223 */ 224 public WebDriver getDriver() { 225 return driver; 226 } 227 228 @Override 229 public String getSessionId() { 230 return sessionId; 231 } 232 233 private String wgetnSaveResourceString(String className, String testName) { 234 String dir = determineSaveDir(className, testName); 235 // http://www.jwz.org/hacks/wgetn 236 return "wgetn https://saucelabs.com/" + sessionId + "/%04dscreenshot.png 0 50"; 237 } 238 239 private String determineSaveDir(String className, String testName) { 240 String dir = deriveResourceBaseNames(className, testName, ""); 241 dir = dir.substring(0, dir.length() -1); 242 return dir; 243 } 244 245 private void writeFile(String fileName, String content) throws IOException { 246 File file = new File(fileName); 247 248 if (!file.exists()) { 249 file.createNewFile(); 250 } 251 252 FileWriter fw = new FileWriter(file.getAbsoluteFile()); 253 BufferedWriter bw = new BufferedWriter(fw); 254 bw.write(content); 255 bw.flush(); 256 bw.close(); 257 } 258 }