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  package edu.samplu.common;
17  
18  import com.saucelabs.common.SauceOnDemandAuthentication;
19  import com.saucelabs.common.SauceOnDemandSessionIdProvider;
20  import com.saucelabs.junit.SauceOnDemandTestWatcher;
21  import com.saucelabs.saucerest.SauceREST;
22  import org.junit.Assert;
23  import org.openqa.selenium.Platform;
24  import org.openqa.selenium.WebDriver;
25  import org.openqa.selenium.ie.InternetExplorerDriver;
26  import org.openqa.selenium.remote.DesiredCapabilities;
27  import org.openqa.selenium.remote.RemoteWebDriver;
28  
29  import java.io.BufferedWriter;
30  import java.io.File;
31  import java.io.FileWriter;
32  import java.io.IOException;
33  import java.net.URL;
34  import java.util.HashMap;
35  import java.util.Map;
36  
37  /**
38   * 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>.
39   *
40   * 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.
41   *
42   * In order to use the {@link SauceOnDemandTestWatcher}, the test must implement the {@link SauceOnDemandSessionIdProvider} interface.
43   *
44   */
45  public class SauceLabsWebDriverHelper implements SauceOnDemandSessionIdProvider {
46  
47      /**
48       * remote.driver.saucelabs.share
49       */
50      public static final String SAUCE_SHARE_PROPERTY = "remote.driver.saucelabs.share";
51  
52      /**
53       * remote.driver.saucelabs.pop.disable
54       */
55      public static final String SAUCE_POPUP_PROPERTY = "remote.driver.saucelabs.pop.disable";
56  
57      /**
58       * remote.driver.saucelabs
59       */
60      public static final String SAUCE_PROPERTY = "remote.driver.saucelabs";
61  
62      /**
63       * remote.driver.saucelabs.platform
64       */
65      public static final String SAUCE_PLATFORM_PROPERTY = "remote.driver.saucelabs.platform";
66  
67      /**
68       * remote.driver.saucelabs.browser
69       */
70      public static final String SAUCE_BROWSER_PROPERTY = "remote.driver.saucelabs.browser";
71  
72      /**
73       * remote.driver.saucelabs.user
74       */
75      public static final String SAUCE_USER_PROPERTY = "remote.driver.saucelabs.user";
76  
77      /**
78       * remote.driver.saucelabs.key
79       */
80      public static final String SAUCE_KEY_PROPERTY = "remote.driver.saucelabs.key";
81  
82      /**
83       * remote.driver.saucelabs.version
84       */
85      public static final String SAUCE_VERSION_PROPERTY = "remote.driver.saucelabs.version";
86  
87      /**
88       * Constructs a {@link SauceOnDemandAuthentication} instance using the supplied user name/access key.  To use the authentication
89       * supplied by environment variables or from an external file, use the no-arg {@link SauceOnDemandAuthentication} constructor.
90       */
91      public SauceOnDemandAuthentication authentication = new SauceOnDemandAuthentication(System.getProperty(SAUCE_USER_PROPERTY), System.getProperty(SAUCE_KEY_PROPERTY));
92  
93      private WebDriver driver;
94  
95      private String sessionId;
96  
97      /**
98       * Saucelabs setup
99       * @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", version);
138         }
139 
140         capabilities.setCapability("platform", System.getProperty(SAUCE_PLATFORM_PROPERTY, Platform.UNIX.toString()).replaceAll("_", " "));
141         capabilities.setCapability("idle-timeout", 180);
142         capabilities.setCapability("max-duration", 480);
143         capabilities.setCapability("name",  className + "." + testName + "-" + ITUtil.DTS);
144         capabilities.setCapability("disable-popup-handler", System.getProperty(SAUCE_POPUP_PROPERTY, "false"));
145         capabilities.setCapability("public", System.getProperty(SAUCE_SHARE_PROPERTY, "share"));
146 
147         this.driver = new RemoteWebDriver(
148                 new URL("http://" + authentication.getUsername() + ":" + authentication.getAccessKey() + "@ondemand.saucelabs.com:80/wd/hub"),
149                 capabilities);
150         this.sessionId = ((RemoteWebDriver)driver).getSessionId().toString();
151 
152         // TODO it would be better to do these at tear down, passing state could then be included in names, but requires more parameters
153         try {
154             String dir = determineSaveDir(className, testName);
155             String resources = "mkdir " + dir + " ; cd " + dir + " ; \n"
156                     + curlSaveResourceString(className, testName, "selenium-server.log") + " ; \n"
157                     + curlSaveResourceString(className, testName, "video.flv") + " ; \n"
158                     + wgetnSaveResourceString(className, testName) + " ; \n"
159                     + "cd ../\n";
160             System.out.println(resources);
161             writeFile("SauceLabsResources" + dir + ".sh", resources);
162         } catch (Exception e) {
163             System.out.println("Exception while writing SauceLabsResources.sh " + e.getMessage());
164             System.out.println(curlSaveResourceString(className, testName, "selenium-server.log"));
165             System.out.println(curlSaveResourceString(className, testName, "video.flv"));
166             //            System.out.println(curlSaveResourceString(className, testName, "XXXXscreenshot.png (where XXXX is a number between 0000 and 9999)")); // TODO
167         }
168     }
169 
170     /**
171      * Do Suacelabs related teardown things.  Mostly flag the tests as passed or failed.
172      * @param passed
173      * @param sessionId
174      * @param sauceUser
175      * @param sauceKey
176      * @throws Exception
177      */
178     public static void tearDown(boolean passed, String sessionId, String sauceUser, String sauceKey) throws Exception {
179         if (sessionId != null && System.getProperty(SAUCE_PROPERTY) != null) {
180             SauceREST client = new SauceREST(sauceUser, sauceKey);
181             /* Using a map of udpates:
182             * (http://saucelabs.com/docs/sauce-ondemand#alternative-annotation-methods)
183             */
184             Map<String, Object> updates = new HashMap<String, Object>();
185             updates.put("passed", passed);
186             updates.put("build", System.getProperty("rice.version", "unknown"));
187             client.updateJobInfo(sessionId, updates);
188 
189             if (passed) {
190                 System.out.println("Registering session passed " + sessionId);
191                 client.jobPassed(sessionId);
192             } else {
193                 System.out.println("Registering session failed " + sessionId);
194                 client.jobFailed(sessionId);
195             }
196 
197             Thread.sleep(5000); // give the client message a chance to get processed on saucelabs side
198         }
199     }
200 
201     private String curlSaveResourceString(String className, String testName, String resource) {
202         return "curl -o " + deriveResourceBaseNames(className, testName, resource)
203                 + " -u " + authentication.getUsername() + ":" + authentication.getAccessKey()
204                 + " http://saucelabs.com/rest/" + authentication.getUsername()+ "/jobs/" + sessionId + "/results/" + resource;
205     }
206 
207     private String deriveResourceBaseNames(String className, String testName, String resource) {
208         return className + "." + testName + "-"
209                 + System.getProperty(SAUCE_PLATFORM_PROPERTY, Platform.UNIX.toString()) + "-"
210                 + System.getProperty(SAUCE_BROWSER_PROPERTY) + "-"
211                 + System.getProperty(SAUCE_VERSION_PROPERTY) + "-"
212                 + System.getProperty(WebDriverLegacyITBase.REMOTE_PUBLIC_USER_PROPERTY, "admin") + "-"
213                 + System.getProperty("rice.version", "unknown_build") + "-"
214                 + ITUtil.DTS + "-"
215                 + resource;
216     }
217 
218     /**
219      * Returns the driver
220      * @return WebDriver
221      */
222     public WebDriver getDriver() {
223         return driver;
224     }
225 
226     @Override
227     public String getSessionId() {
228         return sessionId;
229     }
230 
231     private String wgetnSaveResourceString(String className, String testName) {
232         String dir = determineSaveDir(className, testName);
233         // http://www.jwz.org/hacks/wgetn
234         return "wgetn https://saucelabs.com/rest/" + authentication.getUsername()+ "/jobs/"
235                 + sessionId + "/results/%04dscreenshot.jpg 0 50";
236     }
237 
238     private String determineSaveDir(String className, String testName) {
239         String dir = deriveResourceBaseNames(className, testName, "");
240         dir = dir.substring(0, dir.length() -1);
241         return dir;
242     }
243 
244     private void writeFile(String fileName, String content) throws IOException {
245         File file = new File(fileName);
246 
247         if (!file.exists()) {
248             file.createNewFile();
249         }
250 
251         FileWriter fw = new FileWriter(file.getAbsoluteFile());
252         BufferedWriter bw = new BufferedWriter(fw);
253         bw.write(content);
254         bw.flush();
255         bw.close();
256     }
257 }