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 }