├── .gitignore ├── .sauce.yml ├── .travis.yml ├── Jenkinsfile ├── README.md ├── pom.xml └── src └── test └── java └── com └── yourcompany ├── Pages └── GuineaPigPage.java └── Tests ├── FollowLinkTest.java ├── TestBase.java ├── TextInputTest.java └── W3CTestNG.java /.gitignore: -------------------------------------------------------------------------------- 1 | ### Java template 2 | *.class 3 | 4 | # Mobile Tools for Java (J2ME) 5 | .mtj.tmp/ 6 | 7 | # Package Files # 8 | *.jar 9 | *.war 10 | *.ear 11 | 12 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 13 | hs_err_pid* 14 | 15 | # Created by .ignore support plugin (hsz.mobi) 16 | 17 | target/ 18 | *.iml 19 | .idea -------------------------------------------------------------------------------- /.sauce.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: "java" 3 | maven-version: "3.3" 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | before_install: 5 | - export | grep SAUCE_ 6 | script: mvn clean install 7 | env: 8 | global: 9 | - SAUCE_USERNAME=sauce_examples 10 | addons: 11 | jwt: 12 | secure: U+Jj1AtsbfuQU4u9vKjDuXX8Q1CiGv8mNONk2xSF7f9cMBOBru1KsW+uOlc/6CnfNBpgll8dD4qBDIn8tbp6e4+XglG/8Fwds5vRED+Wdtgav4UH8OEg7p4tW/Gg3dYCrcKdTdydQ5uHKPWgONZ4Pdxsf5LqDuo995gXpF0UJudhj+72URYY9Wyk5AT0tkFGsGHFE7DhisqTFmjlXVM6jxONJdrlRMImIIYApFddWDJQr+h+H0ty8/NYevcedFu4hgjp4O1HgkAilj/LJ1sd5YQtNk5oaJDdr1w1lT8/3D5TOkzcQB1DMUuBwVd907lOUfdElOKpDwryTXkijPwTLwQqGLVXoOD2qK0FB4p0sUHCSrkGOza/VWdnrbMBS0RIUQwWBT5M5NOYgl7rZVK/BAPrrVFIDdGcjScZvwnp0Z1IVfer3YVt8D7b1B5LJklOupRIekwzpX4X2uKQ1iTfFxr8EgrZKFPGkwWpNEOPt/m0d9zRjwmBOHYBBEJJtfGIo40JHckOiH6gfYqYfxGTIEdO2Cz6cOaQWNsNdb2Tfe7dv33/5OMm9cXY5UAk2DKXuMujim9dl3tI1TCc1YjoqNJ8i91/+W6UGsKSc0kf6cztSd7DSB6N/BUs/lN8YFipN9HzCKGRReIZShCWTxuW+lH4AyG+mzCgyr/h81xnGvs= 13 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | node { 2 | // Mark the code checkout 'stage'.... 3 | stage 'Checkout' 4 | // Get some code from a GitHub repository 5 | checkout scm 6 | // Note: if this is copy and pasted into pipeline script, the following will work while the above handles branches and such 7 | // git url: 'https://github.com/saucelabs-sample-test-frameworks/Java-TestNG-Selenium.git' 8 | 9 | docker.image('maven:3.3.9-jdk-7').inside { 10 | stage 'Compile' 11 | sh "mvn compile" 12 | stage 'Test' 13 | sauce('saucelabs') { 14 | sauceconnect(useGeneratedTunnelIdentifier: true, verboseLogging: true) { 15 | sh "mvn test" 16 | } 17 | } 18 | } 19 | 20 | stage 'Collect Results' 21 | step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml']) 22 | step([$class: 'SauceOnDemandTestPublisher']) 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Java-TestNg-Selenium 2 | [![Travis Status](https://travis-ci.org/saucelabs-sample-test-frameworks/Java-TestNG-Selenium.svg?branch=master)](https://travis-ci.org/saucelabs-sample-test-frameworks/Java-TestNG-Selenium) 3 | 4 | This code is provided on an "AS-IS” basis without warranty of any kind, either express or implied, including without limitation any implied warranties of condition, uninterrupted use, merchantability, fitness for a particular purpose, or non-infringement. Your tests and testing environments may require you to modify this framework. Issues regarding this framework should be submitted through GitHub. For questions regarding Sauce Labs integration, please see the Sauce Labs documentation at https://wiki.saucelabs.com/. This framework is not maintained by Sauce Labs Support. 5 | 6 | ### Environment Setup 7 | 8 | 1. Global Dependencies 9 | * [Install Maven](https://maven.apache.org/install.html) 10 | * Or Install Maven with [Homebrew](http://brew.sh/) 11 | ``` 12 | $ brew install maven 13 | ``` 14 | 2. Sauce Credentials 15 | * In the terminal export your Sauce Labs Credentials as environmental variables: 16 | ``` 17 | $ export SAUCE_USERNAME= 18 | $ export SAUCE_ACCESS_KEY= 19 | ``` 20 | 3. Project Dependencies 21 | * Check that Packages are available 22 | ``` 23 | $ cd Java-TestNg-Selenium 24 | $ mvn test-compile 25 | ``` 26 | * You may also want to run the command below to check for outdated dependencies. Please be sure to verify and review updates before editing your pom.xml file. The updated packages may or may not be compatible with your code. 27 | ``` 28 | $ mvn versions:display-dependency-updates 29 | ``` 30 | ### Running Tests 31 | 32 | Tests in Parallel: 33 | ``` 34 | $ mvn test 35 | ``` 36 | 37 | [Sauce Labs Dashboard](https://saucelabs.com/beta/dashboard) 38 | 39 | ### Advice/Troubleshooting 40 | 1. It may be useful to use a Java IDE such as IntelliJ or Eclipse to help troubleshoot potential issues. 41 | 2. There may be additional latency when using a remote webdriver to run tests on Sauce Labs. Timeouts or Waits may need to be increased. 42 | * [Selenium tips regarding explicit waits](https://wiki.saucelabs.com/display/DOCS/Best+Practice%3A+Use+Explicit+Waits) 43 | 44 | ### Resources 45 | ##### [Sauce Labs Documentation](https://wiki.saucelabs.com/) 46 | 47 | ##### [SeleniumHQ Documentation](http://www.seleniumhq.org/docs/) 48 | 49 | ##### [TestNg Documentation](http://testng.org/javadocs/index.html) 50 | 51 | ##### [Java Documentation](https://docs.oracle.com/javase/7/docs/api/) 52 | 53 | ##### Stack Overflow: 54 | * [Related Stack Overflow Threads](http://stackoverflow.com/questions/27355003/advise-on-hierarchy-for-element-locators-in-selenium-webdriver) 55 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | sauce-project 6 | com.yourcompany 7 | 1.0-SNAPSHOT 8 | jar 9 | sauce_quickstart 10 | A sample Maven project that demonstrates how to integrate Sauce OnDemand with WebDriver tests 11 | that run using TestNG 12 | 13 | 14 | 15 | 16 | org.hamcrest 17 | hamcrest-core 18 | 1.3 19 | test 20 | 21 | 22 | org.testng 23 | testng 24 | 6.10 25 | test 26 | 27 | 28 | org.seleniumhq.selenium 29 | selenium-java 30 | 3.11.0 31 | test 32 | 33 | 34 | commons-lang 35 | commons-lang 36 | 2.6 37 | test 38 | 39 | 40 | com.saucelabs 41 | sauce_testng 42 | 2.1.23 43 | test 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | maven-compiler-plugin 52 | 3.0 53 | 54 | 1.7 55 | 1.7 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-surefire-plugin 61 | 2.12.4 62 | 63 | classes 64 | 40 65 | false 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/test/java/com/yourcompany/Pages/GuineaPigPage.java: -------------------------------------------------------------------------------- 1 | package com.yourcompany.Pages; 2 | 3 | import org.openqa.selenium.WebDriver; 4 | import org.openqa.selenium.WebElement; 5 | import org.openqa.selenium.support.FindBy; 6 | import org.openqa.selenium.support.PageFactory; 7 | import org.openqa.selenium.support.ui.ExpectedConditions; 8 | import org.openqa.selenium.support.ui.WebDriverWait; 9 | 10 | public class GuineaPigPage { 11 | 12 | @FindBy(linkText = "i am a link") 13 | private WebElement theActiveLink; 14 | 15 | @FindBy(id = "your_comments") 16 | private WebElement yourCommentsSpan; 17 | 18 | @FindBy(id = "comments") 19 | private WebElement commentsTextAreaInput; 20 | 21 | @FindBy(id = "submit") 22 | private WebElement submitButton; 23 | 24 | public WebDriver driver; 25 | public static String url = "https://saucelabs-sample-test-frameworks.github.io/training-test-page"; 26 | 27 | public static GuineaPigPage visitPage(WebDriver driver) { 28 | GuineaPigPage page = new GuineaPigPage(driver); 29 | page.visitPage(); 30 | return page; 31 | } 32 | 33 | public GuineaPigPage(WebDriver driver) { 34 | this.driver = driver; 35 | PageFactory.initElements(driver, this); 36 | } 37 | 38 | public void visitPage() { 39 | this.driver.get(url); 40 | } 41 | 42 | public void followLink() { 43 | theActiveLink.click(); 44 | } 45 | 46 | public void submitComment(String text) { 47 | commentsTextAreaInput.sendKeys(text); 48 | submitButton.click(); 49 | } 50 | 51 | public String getSubmittedCommentText() { 52 | return yourCommentsSpan.getText(); 53 | } 54 | 55 | public boolean isOnPage() { 56 | String title = "I am a page title - Sauce Labs"; 57 | return driver.getTitle() == title; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/com/yourcompany/Tests/FollowLinkTest.java: -------------------------------------------------------------------------------- 1 | package com.yourcompany.Tests; 2 | 3 | import com.yourcompany.Pages.GuineaPigPage; 4 | import org.openqa.selenium.InvalidElementStateException; 5 | import org.openqa.selenium.WebDriver; 6 | import org.testng.Assert; 7 | import org.testng.annotations.Test; 8 | 9 | import java.lang.reflect.Method; 10 | import java.net.MalformedURLException; 11 | import java.rmi.UnexpectedException; 12 | 13 | /** 14 | * Created by mehmetgerceker on 12/7/15. 15 | */ 16 | 17 | public class FollowLinkTest extends TestBase { 18 | 19 | /** 20 | * Runs a simple test verifying link can be followed. 21 | * 22 | * @throws InvalidElementStateException 23 | */ 24 | @Test(dataProvider = "hardCodedBrowsers") 25 | public void verifyLinkTest(String browser, String version, String os, Method method) 26 | throws MalformedURLException, InvalidElementStateException, UnexpectedException { 27 | 28 | //create webdriver session 29 | this.createDriver(browser, version, os, method.getName()); 30 | WebDriver driver = this.getWebDriver(); 31 | 32 | this.annotate("Visiting GuineaPig page..."); 33 | GuineaPigPage page = GuineaPigPage.visitPage(driver); 34 | 35 | this.annotate("Clicking on link..."); 36 | page.followLink(); 37 | 38 | this.annotate("Asserting that we are on a new page..."); 39 | Assert.assertFalse(page.isOnPage()); 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/test/java/com/yourcompany/Tests/TestBase.java: -------------------------------------------------------------------------------- 1 | package com.yourcompany.Tests; 2 | 3 | import com.saucelabs.common.SauceOnDemandAuthentication; 4 | import com.saucelabs.common.SauceOnDemandSessionIdProvider; 5 | import com.saucelabs.testng.SauceOnDemandAuthenticationProvider; 6 | import com.saucelabs.testng.SauceOnDemandTestListener; 7 | import org.openqa.selenium.JavascriptExecutor; 8 | import org.openqa.selenium.WebDriver; 9 | import org.openqa.selenium.remote.CapabilityType; 10 | import org.openqa.selenium.remote.DesiredCapabilities; 11 | import org.openqa.selenium.remote.RemoteWebDriver; 12 | import org.testng.ITestResult; 13 | import org.testng.annotations.AfterMethod; 14 | import org.testng.annotations.DataProvider; 15 | import org.testng.annotations.Listeners; 16 | 17 | import java.lang.reflect.Method; 18 | import java.net.MalformedURLException; 19 | import java.net.URL; 20 | import java.rmi.UnexpectedException; 21 | 22 | /** 23 | * Simple TestNG test which demonstrates being instantiated via a DataProvider in order to supply multiple browser combinations. 24 | * 25 | * @author Neil Manvar 26 | */ 27 | public class TestBase { 28 | 29 | public String buildTag = System.getenv("BUILD_TAG"); 30 | 31 | public String username = System.getenv("SAUCE_USERNAME"); 32 | 33 | public String accesskey = System.getenv("SAUCE_ACCESS_KEY"); 34 | 35 | /** 36 | * ThreadLocal variable which contains the {@link WebDriver} instance which is used to perform browser interactions with. 37 | */ 38 | private ThreadLocal webDriver = new ThreadLocal(); 39 | 40 | /** 41 | * ThreadLocal variable which contains the Sauce Job Id. 42 | */ 43 | private ThreadLocal sessionId = new ThreadLocal(); 44 | 45 | /** 46 | * DataProvider that explicitly sets the browser combinations to be used. 47 | * 48 | * @param testMethod 49 | * @return Two dimensional array of objects with browser, version, and platform information 50 | */ 51 | @DataProvider(name = "hardCodedBrowsers", parallel = true) 52 | public static Object[][] sauceBrowserDataProvider(Method testMethod) { 53 | return new Object[][]{ 54 | new Object[]{"MicrosoftEdge", "18.17763", "Windows 10"}, 55 | new Object[]{"firefox", "latest", "Windows 10"}, 56 | new Object[]{"internet explorer", "11.0", "Windows 8.1"}, 57 | new Object[]{"safari", "12", "macOS 10.13"}, 58 | new Object[]{"chrome", "latest", "macOS 10.13"}, 59 | new Object[]{"firefox", "latest-1", "Windows 10"}, 60 | }; 61 | } 62 | 63 | /** 64 | * @return the {@link WebDriver} for the current thread 65 | */ 66 | public WebDriver getWebDriver() { 67 | return webDriver.get(); 68 | } 69 | 70 | /** 71 | * 72 | * @return the Sauce Job id for the current thread 73 | */ 74 | public String getSessionId() { 75 | return sessionId.get(); 76 | } 77 | 78 | /** 79 | * Constructs a new {@link RemoteWebDriver} instance which is configured to use the capabilities defined by the browser, 80 | * version and os parameters, and which is configured to run against ondemand.saucelabs.com, using 81 | * the username and access key populated by the {@link #authentication} instance. 82 | * 83 | * @param browser Represents the browser to be used as part of the test run. 84 | * @param version Represents the version of the browser to be used as part of the test run. 85 | * @param os Represents the operating system to be used as part of the test run. 86 | * @param methodName Represents the name of the test case that will be used to identify the test on Sauce. 87 | * @return 88 | * @throws MalformedURLException if an error occurs parsing the url 89 | */ 90 | protected void createDriver(String browser, String version, String os, String methodName) 91 | throws MalformedURLException, UnexpectedException { 92 | DesiredCapabilities capabilities = new DesiredCapabilities(); 93 | 94 | // set desired capabilities to launch appropriate browser on Sauce 95 | capabilities.setCapability(CapabilityType.BROWSER_NAME, browser); 96 | capabilities.setCapability(CapabilityType.VERSION, version); 97 | capabilities.setCapability(CapabilityType.PLATFORM, os); 98 | capabilities.setCapability("name", methodName); 99 | 100 | if (buildTag != null) { 101 | capabilities.setCapability("build", buildTag); 102 | } 103 | 104 | // Launch remote browser and set it as the current thread 105 | webDriver.set(new RemoteWebDriver( 106 | new URL("https://" + username + ":" + accesskey + "@ondemand.saucelabs.com/wd/hub"), 107 | capabilities)); 108 | 109 | // set current sessionId 110 | String id = ((RemoteWebDriver) getWebDriver()).getSessionId().toString(); 111 | sessionId.set(id); 112 | } 113 | 114 | /** 115 | * Method that gets invoked after test. 116 | * Dumps browser log and 117 | * Closes the browser 118 | */ 119 | @AfterMethod 120 | public void tearDown(ITestResult result) throws Exception { 121 | ((JavascriptExecutor) webDriver.get()).executeScript("sauce:job-result=" + (result.isSuccess() ? "passed" : "failed")); 122 | webDriver.get().quit(); 123 | } 124 | 125 | protected void annotate(String text) { 126 | ((JavascriptExecutor) webDriver.get()).executeScript("sauce:context=" + text); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/com/yourcompany/Tests/TextInputTest.java: -------------------------------------------------------------------------------- 1 | package com.yourcompany.Tests; 2 | 3 | import com.yourcompany.Pages.GuineaPigPage; 4 | import org.openqa.selenium.InvalidElementStateException; 5 | import org.openqa.selenium.JavascriptExecutor; 6 | import org.openqa.selenium.WebDriver; 7 | import org.testng.Assert; 8 | 9 | import java.lang.reflect.Method; 10 | import java.net.MalformedURLException; 11 | import java.rmi.UnexpectedException; 12 | import java.util.UUID; 13 | 14 | 15 | /** 16 | * Created by mehmetgerceker on 12/7/15. 17 | */ 18 | 19 | public class TextInputTest extends TestBase { 20 | 21 | /** 22 | * Runs a simple test verifying if the comment input is functional. 23 | * @throws InvalidElementStateException 24 | */ 25 | @org.testng.annotations.Test(dataProvider = "hardCodedBrowsers") 26 | public void verifyCommentInputTest(String browser, String version, String os, Method method) 27 | throws MalformedURLException, InvalidElementStateException, UnexpectedException { 28 | this.createDriver(browser, version, os, method.getName()); 29 | WebDriver driver = this.getWebDriver(); 30 | 31 | String commentInputText = UUID.randomUUID().toString(); 32 | 33 | this.annotate("Visiting GuineaPig page..."); 34 | GuineaPigPage page = GuineaPigPage.visitPage(driver); 35 | 36 | this.annotate(String.format("Submitting comment: \"%s\"", commentInputText)); 37 | page.submitComment(commentInputText); 38 | 39 | this.annotate(String.format("Asserting submitted comment is: \"%s\"", commentInputText)); 40 | Assert.assertTrue(page.getSubmittedCommentText().contains(commentInputText)); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /src/test/java/com/yourcompany/Tests/W3CTestNG.java: -------------------------------------------------------------------------------- 1 | package com.yourcompany.Tests; 2 | 3 | import com.yourcompany.Pages.GuineaPigPage; 4 | import org.openqa.selenium.JavascriptExecutor; 5 | import org.openqa.selenium.MutableCapabilities; 6 | import org.openqa.selenium.WebDriver; 7 | import org.openqa.selenium.edge.EdgeOptions; 8 | import org.openqa.selenium.firefox.FirefoxOptions; 9 | import org.openqa.selenium.ie.InternetExplorerOptions; 10 | import org.openqa.selenium.remote.RemoteWebDriver; 11 | import org.openqa.selenium.safari.SafariOptions; 12 | import org.testng.Assert; 13 | import org.testng.ITestResult; 14 | import org.testng.annotations.AfterMethod; 15 | import org.testng.annotations.DataProvider; 16 | import org.testng.annotations.Test; 17 | 18 | import java.net.MalformedURLException; 19 | import java.net.URL; 20 | 21 | public class W3CTestNG { 22 | 23 | private WebDriver driver; 24 | private MutableCapabilities options; 25 | 26 | /** 27 | * Parameters used here represent options for W3C compliant 28 | * browsers (Chrome is currently not compliant) 29 | * @return 30 | */ 31 | @DataProvider(parallel = true) 32 | public static Object[][] data() { 33 | return new Object[][] { 34 | //{new ChromeOptions()}, 35 | {new FirefoxOptions(), "latest", "Windows 10"}, 36 | {new FirefoxOptions(), "58.0", "OS X 10.12"}, 37 | {new EdgeOptions(), "latest", "Windows 10"}, 38 | {new SafariOptions(), "latest", "OS X 10.12"}, 39 | {new InternetExplorerOptions(), "latest", "Windows 7"}, 40 | }; 41 | } 42 | 43 | /** 44 | * Utility function that makes use of W3C Options classes. 45 | * 46 | * @param options 47 | * @param browserVersion 48 | * @param platformName 49 | * @throws MalformedURLException 50 | */ 51 | public void createDriverOptions(MutableCapabilities options, String browserVersion, String platformName) throws MalformedURLException{ 52 | String username = System.getenv("SAUCE_USERNAME"); 53 | String accesskey = System.getenv("SAUCE_ACCESS_KEY"); 54 | 55 | options.setCapability("browserVersion", browserVersion); 56 | options.setCapability("platformName", platformName); 57 | 58 | MutableCapabilities sauceOptions = new MutableCapabilities(); 59 | sauceOptions.setCapability("seleniumVersion", "3.11.0"); 60 | sauceOptions.setCapability("name", "W3CTestNG"); 61 | sauceOptions.setCapability("build", "W3C"); 62 | 63 | options.setCapability("sauce:options", sauceOptions); 64 | 65 | driver = new RemoteWebDriver(new URL("https://" + username + ":" + accesskey + "@ondemand.saucelabs.com/wd/hub"), options); 66 | } 67 | 68 | @AfterMethod 69 | public void teardown(ITestResult result){ 70 | driver.quit(); 71 | } 72 | 73 | @Test(dataProvider = "data") 74 | public void simpleCase(MutableCapabilities options, String browserVersion, String platformName) throws MalformedURLException { 75 | this.createDriverOptions(options, browserVersion, platformName); 76 | 77 | GuineaPigPage page = GuineaPigPage.visitPage(driver); 78 | String title = page.driver.getTitle(); 79 | 80 | Assert.assertTrue(title.contains("Sauce")); 81 | } 82 | } 83 | --------------------------------------------------------------------------------