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    }