diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..09a587d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +.github/* @browserstack/asi-devs + +* @browserstack/automate-public-repos diff --git a/.github/workflows/Semgrep.yml b/.github/workflows/Semgrep.yml new file mode 100644 index 0000000..0347afd --- /dev/null +++ b/.github/workflows/Semgrep.yml @@ -0,0 +1,48 @@ +# Name of this GitHub Actions workflow. +name: Semgrep + +on: + # Scan changed files in PRs (diff-aware scanning): + # The branches below must be a subset of the branches above + pull_request: + branches: ["master", "main"] + push: + branches: ["master", "main"] + schedule: + - cron: '0 6 * * *' + + +permissions: + contents: read + +jobs: + semgrep: + # User definable name of this GitHub Actions job. + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + name: semgrep/ci + # If you are self-hosting, change the following `runs-on` value: + runs-on: ubuntu-latest + + container: + # A Docker image with Semgrep installed. Do not change this. + image: returntocorp/semgrep + + # Skip any PR created by dependabot to avoid permission issues: + if: (github.actor != 'dependabot[bot]') + + steps: + # Fetch project source with GitHub Actions Checkout. + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 + # Run the "semgrep ci" command on the command line of the docker image. + - run: semgrep ci --sarif --output=semgrep.sarif + env: + # Add the rules that Semgrep uses by setting the SEMGREP_RULES environment variable. + SEMGREP_RULES: p/default # more at semgrep.dev/explore + + - name: Upload SARIF file for GitHub Advanced Security Dashboard + uses: github/codeql-action/upload-sarif@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0 + with: + sarif_file: semgrep.sarif + if: always() \ No newline at end of file diff --git a/.github/workflows/maven-workflow-run.yml b/.github/workflows/maven-workflow-run.yml new file mode 100644 index 0000000..4f83b92 --- /dev/null +++ b/.github/workflows/maven-workflow-run.yml @@ -0,0 +1,125 @@ +# This job is to test different maven profiles in sdk branch again Pull Request raised +# This workflow targets Java with Maven execution + +name: Java SDK Test workflow for Maven on workflow_dispatch + +on: + workflow_dispatch: + inputs: + commit_sha: + description: 'The full commit id to build' + required: true + +jobs: + comment-run: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + max-parallel: 3 + matrix: + java: [ '8', '11', '17' ] + os: [ 'macos-latest', 'windows-latest', 'ubuntu-latest' ] + exclude: + - java: '8' + os: 'macos-latest' + name: JUnit Repo ${{ matrix.Java }} - ${{ matrix.os }} Sample + env: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.commit_sha }} + - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 + id: status-check-in-progress + env: + job_name: JUnit Repo ${{ matrix.Java }} - ${{ matrix.os }} Sample + commit_sha: ${{ github.event.inputs.commit_sha }} + with: + github-token: ${{ github.token }} + script: | + const result = await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: process.env.job_name, + head_sha: process.env.commit_sha, + status: 'in_progress' + }).catch((err) => ({status: err.status, response: err.response})); + console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`) + if (result.status !== 201) { + console.log('Failed to create check run') + } + - name: Set up Java + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: ${{ matrix.java }} + - name: Run mvn test for junit-4 + run: | + cd junit-4 + mvn compile + mvn test + - name: Run mvn profile sample for junit-4 + run: | + cd junit-4 + mvn compile + mvn test -P sample + - name: Run mvn profile local for junit-4 + run: | + cd junit-4 + mvn compile + mvn test -P local + - name: Run gradle task sampleTest + run: | + cd junit-4 + gradle clean sampleTest + - name: Run gradle task sampleLocalTest + run: | + cd junit-4 + gradle clean sampleLocalTest + - name: Run mvn test for junit-5 + run: | + cd junit-5 + mvn compile + mvn test + - name: Run mvn profile single for junit-5 + run: | + cd junit-5 + mvn compile + mvn test -P single + - name: Run mvn profile local for junit-5 + run: | + cd junit-5 + mvn compile + mvn test -P local + - name: Run gradle task sampleTest + run: | + cd junit-5 + gradle clean sampleTest + - name: Run gradle task sampleLocalTest + run: | + cd junit-5 + gradle clean sampleLocalTest + - if: always() + uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975 + id: status-check-completed + env: + conclusion: ${{ job.status }} + job_name: JUnit Repo ${{ matrix.Java }} - ${{ matrix.os }} Sample + commit_sha: ${{ github.event.inputs.commit_sha }} + with: + github-token: ${{ github.token }} + script: | + const result = await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: process.env.job_name, + head_sha: process.env.commit_sha, + status: 'completed', + conclusion: process.env.conclusion + }).catch((err) => ({status: err.status, response: err.response})); + console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`) + if (result.status !== 201) { + console.log('Failed to create check run') + } diff --git a/.gitignore b/.gitignore index 4274f56..272a376 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,8 @@ browserstack.err *.iml local.log *.png -*.txt \ No newline at end of file +*.txt +**/logs +bstack_* +gradlew* +gradle diff --git a/README.md b/README.md index dce7f1c..265a739 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![BrowserStack Logo](https://d98b8t1nnulk5.cloudfront.net/production/images/layout/logo-header.png?1469004780) -This repo covers BrowserStack integration with the JUnit frameworks using Selenium 4. More details in the README.md file of the individual frameworks. +This repo covers BrowserStack integration with the JUnit frameworks using Selenium 4 and BrowserStack SDK. More details in the README.md file of the individual frameworks. * [JUnit 4](https://github.com/browserstack/junit-browserstack/blob/master/junit-4/README.md) * [JUnit 5](https://github.com/browserstack/junit-browserstack/blob/master/junit-5/README.md) diff --git a/junit-4/README.md b/junit-4/README.md index d3dedb6..4aa029c 100644 --- a/junit-4/README.md +++ b/junit-4/README.md @@ -5,17 +5,36 @@ ![JUnit](http://junit.org/junit4/images/junit-logo.png) -## Setup +## Using Maven + +### Setup * Clone the repo * Install dependencies `mvn install` -* Update *.conf.json files inside the `src/test/resources/conf` directory with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings). +* Update `browserstack.yml` files inside the root directory with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings). -## Running your tests -* To run tests, run `mvn test -P parallel` -* To run local tests, run `mvn test -P local` +### Running your tests +* To run a parallel test, run `mvn test -P sample` +* To run local tests, set `browserStackLocal: true` in `browserstack.yml` and run `mvn test -P local` Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github) + +## Using Gradle + +### Prerequisites +- If using Gradle, Java v9+ is required. + +### Setup +* Clone the repo +* Update `browserstack.yml` files inside the root directory with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings). + +### Running your tests +* To run a parallel test, run `gradle sampleTest` +* To run local tests, set `browserStackLocal: true` in `browserstack.yml` and run `gradle sampleLocalTest` + +Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github) + + ## Notes * You can view your test results on the [BrowserStack Automate dashboard](https://www.browserstack.com/automate) * To test on a different set of browsers, check out our [platform configurator](https://www.browserstack.com/automate/java#setting-os-and-browser) @@ -32,9 +51,3 @@ set BROWSERSTACK_USERNAME= set BROWSERSTACK_ACCESS_KEY= ``` - -## Addtional Resources -* [Documentation for writing Automate test scripts in Java](https://www.browserstack.com/automate/java) -* [Customizing your tests on BrowserStack](https://www.browserstack.com/automate/capabilities) -* [Browsers & mobile devices for selenium testing on BrowserStack](https://www.browserstack.com/list-of-browsers-and-platforms?product=automate) -* [Using REST API to access information about your tests via the command-line interface](https://www.browserstack.com/automate/rest-api) diff --git a/junit-4/browserstack.yml b/junit-4/browserstack.yml new file mode 100644 index 0000000..2d769ca --- /dev/null +++ b/junit-4/browserstack.yml @@ -0,0 +1,78 @@ +# ============================= +# Set BrowserStack Credentials +# ============================= +# Add your BrowserStack userName and accessKey here or set BROWSERSTACK_USERNAME and +# BROWSERSTACK_ACCESS_KEY as env variables +userName: YOUR_USERNAME +accessKey: YOUR_ACCESS_KEY + +# ====================== +# BrowserStack Reporting +# ====================== +# The following capabilities are used to set up reporting on BrowserStack: +# Set 'projectName' to the name of your project. Example, Marketing Website +projectName: BrowserStack Samples +# Set `buildName` as the name of the job / testsuite being run +buildName: browserstack build +# `buildIdentifier` is a unique id to differentiate every execution that gets appended to +# buildName. Choose your buildIdentifier format from the available expressions: +# ${BUILD_NUMBER} (Default): Generates an incremental counter with every execution +# ${DATE_TIME}: Generates a Timestamp with every execution. Eg. 05-Nov-19:30 +# Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests +buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression} +# Set `framework` of your test suite. Example, `testng`, `cucumber`, `cucumber-testng` +# This property is needed to send test context to BrowserStack (test name, status) +framework: junit4 + +source: junit4:sample-sdk:v1.0 + +# ======================================= +# Platforms (Browsers / Devices to test) +# ======================================= +# Platforms object contains all the browser / device combinations you want to test on. +# Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate) +platforms: + - os: OS X + osVersion: Big Sur + browserName: Chrome + browserVersion: latest + - os: Windows + osVersion: 10 + browserName: Edge + browserVersion: latest + - deviceName: Samsung Galaxy S22 Ultra + browserName: chrome # Try 'samsung' for Samsung browser + osVersion: 12.0 + +# ======================= +# Parallels per Platform +# ======================= +# The number of parallel threads to be used for each platform set. +# BrowserStack's SDK runner will select the best strategy based on the configured value +# +# Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack +# +# Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack +parallelsPerPlatform: 1 + +# ========================================== +# BrowserStack Local +# (For localhost, staging/private websites) +# ========================================== +# Set browserStackLocal to true if your website under test is not accessible publicly over the internet +# Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction +browserstackLocal: true # (Default false) + +# Options to be passed to BrowserStack local in-case of advanced configurations +# browserStackLocalOptions: + # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local. + # forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. + # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections + +# =================== +# Debugging features +# =================== +debug: false # # Set to true if you need screenshots for every selenium command ran +networkLogs: false # Set to true to enable HAR logs capturing +consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors) +# Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) diff --git a/junit-4/build.gradle b/junit-4/build.gradle new file mode 100644 index 0000000..fce6feb --- /dev/null +++ b/junit-4/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'java' + id 'com.browserstack.gradle-sdk' version "1.1.2" // sdk plugin +} + +repositories { mavenCentral() } + +dependencies { + implementation 'junit:junit:4.13.2' + implementation 'org.seleniumhq.selenium:selenium-java:4.1.4' + implementation 'org.yaml:snakeyaml:2.2' + implementation 'com.browserstack:browserstack-java-sdk:latest.release' +} + +group = 'com.browserstack' +version = '1.0-SNAPSHOT' +description = 'junit-browserstack' +sourceCompatibility = '1.8' + +def browserstackSDKArtifact = configurations.compileClasspath.resolvedConfiguration.resolvedArtifacts.find { it.name == 'browserstack-java-sdk' } + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +tasks.withType(Test) { + systemProperties = System.properties + jvmArgs += "-javaagent:${browserstackSDKArtifact.file}" +} + +task sampleTest(type: Test) { + useJUnit() { + dependsOn cleanTest + include '**/*BStackSampleTest.*' + } +} + +task sampleLocalTest(type: Test) { + useJUnit() { + dependsOn cleanTest + include '**/*BStackLocalTest.*' + } +} diff --git a/junit-4/pom.xml b/junit-4/pom.xml index eadcf95..7841694 100644 --- a/junit-4/pom.xml +++ b/junit-4/pom.xml @@ -12,10 +12,9 @@ UTF-8 - 2.19.1 - - + 3.0.0-M5 default + 2.2 1.8 1.8 @@ -25,7 +24,7 @@ junit junit - 4.13 + 4.13.2 commons-io @@ -35,12 +34,7 @@ org.seleniumhq.selenium selenium-java - 4.0.0 - - - com.browserstack - browserstack-local-java - 1.0.6 + 4.1.4 com.googlecode.json-simple @@ -48,26 +42,50 @@ 1.1.1 - org.apache.httpcomponents - httpclient - 4.5.13 - test + org.yaml + snakeyaml + ${snakeyaml.version} + + + com.browserstack + browserstack-java-sdk + LATEST + compile + + org.apache.maven.plugins + maven-dependency-plugin + + + getClasspathFilenames + + properties + + + + org.apache.maven.plugins maven-surefire-plugin - 2.12.4 + ${surefire.version} + + + org.apache.maven.surefire + surefire-junit47 + ${surefire.version} + + - com/browserstack/SingleTest.java + com/browserstack/BStackSampleTest.java - - parallel.conf.json - + + -javaagent:${com.browserstack:browserstack-java-sdk:jar} + @@ -75,7 +93,7 @@ - single + sample @@ -83,11 +101,11 @@ maven-surefire-plugin - com/browserstack/SingleTest.java + com/browserstack/BStackSampleTest.java - - single.conf.json - + + -javaagent:${com.browserstack:browserstack-java-sdk:jar} + @@ -103,31 +121,11 @@ maven-surefire-plugin - com/browserstack/LocalTest.java - - - local.conf.json - - - - - - - - - parallel - - - - org.apache.maven.plugins - maven-surefire-plugin - - - com/browserstack/SingleTest.java + com/browserstack/BStackLocalTest.java - - parallel.conf.json - + + -javaagent:${com.browserstack:browserstack-java-sdk:jar} + diff --git a/junit-4/settings.gradle b/junit-4/settings.gradle new file mode 100644 index 0000000..3e5b662 --- /dev/null +++ b/junit-4/settings.gradle @@ -0,0 +1,15 @@ +pluginManagement { + repositories { + mavenCentral() + mavenLocal() + gradlePluginPortal() + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "com.browserstack.gradle-sdk") { + useModule("com.browserstack:gradle-sdk:1.1.2") + } + } + } +} diff --git a/junit-4/src/test/java/com/browserstack/BStackLocalTest.java b/junit-4/src/test/java/com/browserstack/BStackLocalTest.java new file mode 100644 index 0000000..d57f90c --- /dev/null +++ b/junit-4/src/test/java/com/browserstack/BStackLocalTest.java @@ -0,0 +1,17 @@ +package com.browserstack; + +import org.junit.Test; + +import java.io.IOException; +import java.net.URISyntaxException; + +import static org.junit.Assert.assertTrue; + +public class BStackLocalTest extends BrowserStackJUnitTest { + + @Test + public void test() { + driver.get("http://bs-local.com:45454/"); + assertTrue("Local content not validated!", driver.getTitle().contains("BrowserStack Local")); + } +} diff --git a/junit-4/src/test/java/com/browserstack/BStackSampleTest.java b/junit-4/src/test/java/com/browserstack/BStackSampleTest.java new file mode 100644 index 0000000..f8535f4 --- /dev/null +++ b/junit-4/src/test/java/com/browserstack/BStackSampleTest.java @@ -0,0 +1,27 @@ +package com.browserstack; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; + +import static org.junit.Assert.assertTrue; + +public class BStackSampleTest extends BrowserStackJUnitTest { + + @Test + public void test() { + driver.get("https://bstackdemo.com/"); + final WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + wait.until(ExpectedConditions.titleIs("StackDemo")); + String product_name = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='1']/p"))).getText(); + WebElement cart_btn = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@id='1']/div[4]"))); + cart_btn.click(); + wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("float-cart__content"))); + final String product_in_cart = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='__next']/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]"))).getText(); + assertTrue("Product add to the cart - Failed!", product_name.matches(product_in_cart)); + } +} diff --git a/junit-4/src/test/java/com/browserstack/BrowserStackJUnitTest.java b/junit-4/src/test/java/com/browserstack/BrowserStackJUnitTest.java index f995a5f..f29a840 100644 --- a/junit-4/src/test/java/com/browserstack/BrowserStackJUnitTest.java +++ b/junit-4/src/test/java/com/browserstack/BrowserStackJUnitTest.java @@ -1,150 +1,60 @@ package com.browserstack; -import com.browserstack.local.Local; -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.message.BasicNameValuePair; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; import org.junit.After; import org.junit.Before; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.Parameterized.Parameters; import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.WebDriver; import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.remote.SessionId; +import org.yaml.snakeyaml.Yaml; -import java.io.FileReader; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; +import java.io.File; +import java.io.InputStream; import java.net.URL; -import java.util.*; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; -@RunWith(Parallelized.class) public class BrowserStackJUnitTest { - public static String username, accessKey; - private static JSONObject config; public WebDriver driver; - public HashMap bstackOptions; - private static Object lock = new Object(); - private static Integer parallels = 0; + public static String userName, accessKey; + public static Map browserStackYamlMap; + public static final String USER_DIR = "user.dir"; - @Parameter(value = 0) - public int taskID; - private Local bsLocal; - - @Parameters - public static Iterable data() throws Exception { - List taskIDs = new ArrayList(); - - if (System.getProperty("config") != null) { - JSONParser parser = new JSONParser(); - config = (JSONObject) parser - .parse(new FileReader("src/test/resources/conf/" + System.getProperty("config"))); - int envs = ((JSONArray) config.get("environments")).size(); - - for (int i = 0; i < envs; i++) { - taskIDs.add(i); - } - } - return taskIDs; - } - - public static void mark(SessionId sessionID, String status, String reason) throws URISyntaxException, IOException { - URI uri = new URI("https://" + username + ":" + accessKey + "@api.browserstack.com/automate/sessions/" + sessionID + ".json"); - HttpPut putRequest = new HttpPut(uri); - - ArrayList nameValuePairs = new ArrayList(); - nameValuePairs.add((new BasicNameValuePair("status", status))); - nameValuePairs.add((new BasicNameValuePair("reason", reason))); - putRequest.setEntity(new UrlEncodedFormEntity(nameValuePairs)); - - HttpClientBuilder.create().build().execute(putRequest); + public BrowserStackJUnitTest() { + File file = new File(getUserDir() + "/browserstack.yml"); + this.browserStackYamlMap = convertYamlFileToMap(file, new HashMap<>()); } @Before - @SuppressWarnings("unchecked") public void setUp() throws Exception { - JSONArray envs = (JSONArray) config.get("environments"); - bstackOptions = new HashMap(); - MutableCapabilities capabilities = new MutableCapabilities(); - - Map envCapabilities = (Map) envs.get(taskID); - Iterator> it = envCapabilities.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = it.next(); - if ("bstack:options".equals(pair.getKey())) { - bstackOptions.putAll((Map) pair.getValue()); - } else { - capabilities.setCapability(pair.getKey(), pair.getValue()); - } - } - - Map commonCapabilities = (Map) config.get("capabilities"); - it = commonCapabilities.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = it.next(); - if ("bstack:options".equals(pair.getKey())) { - bstackOptions.putAll((Map) pair.getValue()); - } else { - capabilities.setCapability(pair.getKey(), pair.getValue()); - } - } - bstackOptions.put("source","junit-4:sample-master:v1.1"); - capabilities.setCapability("bstack:options", bstackOptions); - - username = System.getenv("BROWSERSTACK_USERNAME"); - if (username == null) { - username = (String) config.get("userName"); - } - - accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY"); - if (accessKey == null) { - accessKey = (String) config.get("accessKey"); - } - - this.checkAndStartBrowserStackLocal(capabilities, accessKey); - + userName = System.getenv("BROWSERSTACK_USERNAME") != null ? System.getenv("BROWSERSTACK_USERNAME") : (String) browserStackYamlMap.get("userName"); + accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY") != null ? System.getenv("BROWSERSTACK_ACCESS_KEY") : (String) browserStackYamlMap.get("accessKey"); + HashMap bStackOptions = new HashMap<>(); + bStackOptions.put("source", "junit4:sample-master:v1.2"); + capabilities.setCapability("bstack:options", bStackOptions); driver = new RemoteWebDriver( - new URL("https://" + username + ":" + accessKey + "@" + config.get("server") + "/wd/hub"), capabilities); + new URL(String.format("https://%s:%s@hub.browserstack.com/wd/hub", userName , accessKey)), capabilities); } - public void checkAndStartBrowserStackLocal(MutableCapabilities capabilities, String accessKey) throws Exception { - if (bsLocal != null) { - return; - } - JSONObject localCaps = new JSONObject(bstackOptions); + @After + public void tearDown() { + driver.quit(); + } - synchronized (lock) { - parallels++; - if ((bsLocal == null || !bsLocal.isRunning()) && capabilities.getCapability("bstack:options") != null - && localCaps.get("local") != null - && ((Boolean) localCaps.get("local")) == true) { - bsLocal = new Local(); - Map options = new HashMap(); - options.put("key", accessKey); - try { - bsLocal.start(options); - } catch (Exception e){ - e.printStackTrace(); - } - } - } + private String getUserDir() { + return System.getProperty(USER_DIR); } - @After - public void tearDown() throws Exception { - synchronized (lock){ - parallels--; - driver.quit(); - if (bsLocal != null && parallels == 0) bsLocal.stop(); + private Map convertYamlFileToMap(File yamlFile, Map map) { + try { + InputStream inputStream = Files.newInputStream(yamlFile.toPath()); + Yaml yaml = new Yaml(); + Map config = yaml.load(inputStream); + map.putAll(config); + } catch (Exception e) { + throw new RuntimeException(String.format("Malformed browserstack.yml file - %s.", e)); } + return map; } } diff --git a/junit-4/src/test/java/com/browserstack/LocalTest.java b/junit-4/src/test/java/com/browserstack/LocalTest.java deleted file mode 100644 index 8c8e2ac..0000000 --- a/junit-4/src/test/java/com/browserstack/LocalTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.browserstack; - -import org.junit.Test; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.remote.SessionId; - -import java.io.IOException; -import java.net.URISyntaxException; - -import static org.junit.Assert.assertTrue; - -public class LocalTest extends BrowserStackJUnitTest { - - @Test - public void test() throws URISyntaxException, IOException { - SessionId sessionId = ((RemoteWebDriver) driver).getSessionId(); - try { - driver.get("http://bs-local.com:45691/check"); - - assertTrue(driver.getPageSource().contains("Up and running")); - mark(sessionId, "passed", "Local content validated!"); - - } catch (Throwable t) { - mark(sessionId, "failed", "Local content not validated!"); - System.out.println("Exception: " + t); - } - } -} diff --git a/junit-4/src/test/java/com/browserstack/Parallelized.java b/junit-4/src/test/java/com/browserstack/Parallelized.java deleted file mode 100644 index 33a918b..0000000 --- a/junit-4/src/test/java/com/browserstack/Parallelized.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.browserstack; - -import org.junit.runners.Parameterized; -import org.junit.runners.model.RunnerScheduler; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -public class Parallelized extends Parameterized { - - @SuppressWarnings("rawtypes") - public Parallelized(Class klass) throws Throwable { - super(klass); - setScheduler(new ThreadPoolScheduler()); - } - - private static class ThreadPoolScheduler implements RunnerScheduler { - private final ExecutorService executor; - - public ThreadPoolScheduler() { - String threads = System.getProperty("junit.parallel.threads", "16"); - int numThreads = Integer.parseInt(threads); - executor = Executors.newFixedThreadPool(numThreads); - } - - @Override - public void finished() { - executor.shutdown(); - try { - executor.awaitTermination(10, TimeUnit.MINUTES); - } catch (InterruptedException exc) { - throw new RuntimeException(exc); - } - } - - @Override - public void schedule(Runnable childStatement) { - executor.submit(childStatement); - } - } -} diff --git a/junit-4/src/test/java/com/browserstack/SingleTest.java b/junit-4/src/test/java/com/browserstack/SingleTest.java deleted file mode 100644 index e1e09a3..0000000 --- a/junit-4/src/test/java/com/browserstack/SingleTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.browserstack; - -import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.remote.SessionId; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; - -import java.io.IOException; -import java.net.URISyntaxException; - -import static org.junit.Assert.assertTrue; - -public class SingleTest extends BrowserStackJUnitTest { - - @Test - public void test() throws URISyntaxException, IOException { - SessionId sessionId = ((RemoteWebDriver) driver).getSessionId(); - try { - driver.get("https://bstackdemo.com/"); - final WebDriverWait wait = new WebDriverWait(driver, 10); - wait.until(ExpectedConditions.titleIs("StackDemo")); - String product_name = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='1']/p"))).getText(); - WebElement cart_btn = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@id='1']/div[4]"))); - cart_btn.click(); - wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".float\\-cart__content"))); - final String product_in_cart = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='__next']/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]"))).getText(); - assertTrue(product_name.matches(product_in_cart)); - mark(sessionId, "passed", "Product has been successfully added to the cart!"); - } catch (Throwable t) { - mark(sessionId, "failed", "There was some issue!"); - System.out.println("Exception: " + t); - } - } -} diff --git a/junit-4/src/test/resources/conf/local.conf.json b/junit-4/src/test/resources/conf/local.conf.json deleted file mode 100644 index ad80414..0000000 --- a/junit-4/src/test/resources/conf/local.conf.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "server": "hub.browserstack.com", - "userName": "BROWSERSTACK_USERNAME", - "accessKey": "BROWSERSTACK_ACCESS_KEY", - "capabilities": { - "bstack:options": { - "sessionName": "BStack local junit-4", - "buildName": "browserstack-build-1", - "projectName": "My Test Project", - "debug": true, - "local": true, - "seleniumVersion": "4.0.0" - } - }, - "environments": [ - { - "bstack:options": { - "os": "Windows", - "osVersion": "10" - }, - "browserName": "chrome", - "browserVersion": "latest-1" - } - ] -} diff --git a/junit-4/src/test/resources/conf/parallel.conf.json b/junit-4/src/test/resources/conf/parallel.conf.json deleted file mode 100644 index 41d16e2..0000000 --- a/junit-4/src/test/resources/conf/parallel.conf.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "server": "hub.browserstack.com", - "userName": "BROWSERSTACK_USERNAME", - "accessKey": "BROWSERSTACK_ACCESS_KEY", - "capabilities": { - "bstack:options": { - "sessionName": "BStack parallel junit-4", - "buildName": "browserstack-build-1", - "projectName": "My Test Project", - "debug": true, - "seleniumVersion": "4.0.0" - } - }, - "environments": [ - { - "browserName": "chrome", - "bstack:options": { - "os": "OS X", - "osVersion": "Big Sur" - } - }, - { - "browserName": "safari", - "browserVersion": "latest", - "bstack:options": { - "os": "OS X", - "osVersion": "Monterey" - } - }, - { - "bstack:options": { - "deviceName": "iPhone 12" - } - } - ] -} diff --git a/junit-4/src/test/resources/conf/single.conf.json b/junit-4/src/test/resources/conf/single.conf.json deleted file mode 100644 index 3183036..0000000 --- a/junit-4/src/test/resources/conf/single.conf.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "server": "hub.browserstack.com", - "userName": "BROWSERSTACK_USERNAME", - "accessKey": "BROWSERSTACK_ACCESS_KEY", - "capabilities": { - "bstack:options": { - "sessionName": "BStack single junit-4", - "buildName": "browserstack-build-1", - "projectName": "My Test Project", - "seleniumVersion": "4.0.0", - "debug": true - } - }, - "environments": [ - { - "bstack:options": { - "os": "Windows", - "osVersion": "11" - }, - "browserName": "Chrome", - "browserVersion": "latest" - } - ] -} diff --git a/junit-5/README.md b/junit-5/README.md index 8a613c8..bb68ee5 100644 --- a/junit-5/README.md +++ b/junit-5/README.md @@ -4,24 +4,35 @@ Master branch contains **Selenium 4 - W3C protocol** samples, for **Selenium 3** -## Setup +## Using Maven + +### Setup * Clone the repo * Install dependencies `mvn install` -* Update credentials in the `/src/test/resources/caps.json` file with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings). -* The platform details can be modified in the `/src/test/resources/caps.json` file within the respective profile i.e. `single`, `local`, `parallel`. Refer to our [Capabilities Generator](https://www.browserstack.com/automate/capabilities?tag=selenium-4) page for all platform and capabilities related information. -* For parallel testing, control the concurrency by setting the value for `parallel.count`. Junit 5 uses the following properties for parallelism: - ``` - junit.jupiter.execution.parallel.enabled = true - junit.jupiter.execution.parallel.mode.default = concurrent - junit.jupiter.execution.parallel.config.strategy=fixed - junit.jupiter.execution.parallel.config.fixed.parallelism=${parallel.count} - ``` -## Running your tests -* To run tests, run `mvn test -P parallel` +* Update `browserstack.yml` files inside the root directory with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings). + +### Running your tests +* To run a single test, run `mvn test -P single` * To run local tests, run `mvn test -P local` Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github) +## Using Gradle + +### Prerequisites +- If using Gradle, Java v9+ is required. + +### Setup +- Clone the repository +- Install dependencies `gradle build` + +### Running your tests +- To run the single test, run `gradle sampleTest` +- To run local tests, run `gradle sampleLocalTest` + +Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github) + + ## Notes * You can view your test results on the [BrowserStack Automate dashboard](https://www.browserstack.com/automate) * To test on a different set of browsers, check out our [platform configurator](https://www.browserstack.com/automate/java#setting-os-and-browser) @@ -39,13 +50,6 @@ Understand how many parallel sessions you need by using our [Parallel Test Calcu set BROWSERSTACK_ACCESS_KEY= ``` - -## Additional Resources -* [Documentation for writing Automate test scripts in Java](https://www.browserstack.com/automate/java) -* [Customizing your tests on BrowserStack](https://www.browserstack.com/automate/capabilities) -* [Browsers & mobile devices for selenium testing on BrowserStack](https://www.browserstack.com/list-of-browsers-and-platforms?product=automate) -* [Using REST API to access information about your tests via the command-line interface](https://www.browserstack.com/automate/rest-api) - ## Open Issues * Value for `junit.jupiter.execution.parallel.config.fixed.parallelism` gets multiplied in selenium versions above `4.0.0-alpha-3`. Github issue: diff --git a/junit-5/browserstack.yml b/junit-5/browserstack.yml new file mode 100644 index 0000000..071d85a --- /dev/null +++ b/junit-5/browserstack.yml @@ -0,0 +1,78 @@ +# ============================= +# Set BrowserStack Credentials +# ============================= +# Add your BrowserStack userName and accessKey here or set BROWSERSTACK_USERNAME and +# BROWSERSTACK_ACCESS_KEY as env variables +userName: YOUR_USERNAME +accessKey: YOUR_ACCESS_KEY + +# ====================== +# BrowserStack Reporting +# ====================== +# The following capabilities are used to set up reporting on BrowserStack: +# Set 'projectName' to the name of your project. Example, Marketing Website +projectName: BrowserStack Samples +# Set `buildName` as the name of the job / testsuite being run +buildName: browserstack build +# `buildIdentifier` is a unique id to differentiate every execution that gets appended to +# buildName. Choose your buildIdentifier format from the available expressions: +# ${BUILD_NUMBER} (Default): Generates an incremental counter with every execution +# ${DATE_TIME}: Generates a Timestamp with every execution. Eg. 05-Nov-19:30 +# Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests +buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression} +# Set `framework` of your test suite. Example, `testng`, `cucumber`, `cucumber-testng` +# This property is needed to send test context to BrowserStack (test name, status) +framework: junit5 + +source: junit5:sample-sdk:v1.0 + +# ======================================= +# Platforms (Browsers / Devices to test) +# ======================================= +# Platforms object contains all the browser / device combinations you want to test on. +# Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate) +platforms: + - os: OS X + osVersion: Big Sur + browserName: Chrome + browserVersion: latest + - os: Windows + osVersion: 10 + browserName: Edge + browserVersion: latest + - deviceName: Samsung Galaxy S22 Ultra + browserName: chrome # Try 'samsung' for Samsung browser + osVersion: 12.0 + +# ======================= +# Parallels per Platform +# ======================= +# The number of parallel threads to be used for each platform set. +# BrowserStack's SDK runner will select the best strategy based on the configured value +# +# Example 1 - If you have configured 3 platforms and set `parallelsPerPlatform` as 2, a total of 6 (2 * 3) parallel threads will be used on BrowserStack +# +# Example 2 - If you have configured 1 platform and set `parallelsPerPlatform` as 5, a total of 5 (1 * 5) parallel threads will be used on BrowserStack +parallelsPerPlatform: 1 + +# ========================================== +# BrowserStack Local +# (For localhost, staging/private websites) +# ========================================== +# Set browserStackLocal to true if your website under test is not accessible publicly over the internet +# Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction +browserstackLocal: true # (Default false) + +# Options to be passed to BrowserStack local in-case of advanced configurations +# browserStackLocalOptions: + # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local. + # forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel. + # Entire list of arguments available here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections + +# =================== +# Debugging features +# =================== +debug: false # # Set to true if you need screenshots for every selenium command ran +networkLogs: false # Set to true to enable HAR logs capturing +consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors) +# Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors) diff --git a/junit-5/build.gradle b/junit-5/build.gradle new file mode 100644 index 0000000..966f5ed --- /dev/null +++ b/junit-5/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'java' + id 'com.browserstack.gradle-sdk' version "1.1.2" // sdk plugin +} + +repositories { mavenCentral() } + +dependencies { + implementation 'org.junit.jupiter:junit-jupiter-api:5.9.1' + implementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1' + implementation 'org.seleniumhq.selenium:selenium-java:4.1.4' + implementation 'com.browserstack:browserstack-java-sdk:latest.release' +} + +group = 'com.browserstack' +version = '1.0-SNAPSHOT' +description = 'junit-browserstack' +sourceCompatibility = '1.8' + +def browserstackSDKArtifact = configurations?.compileClasspath?.resolvedConfiguration?.resolvedArtifacts?.find { it.name == 'browserstack-java-sdk' } + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +tasks.withType(Test).configureEach { task -> + browserstackSDKArtifact?.file?.with { + task.systemProperties = System.properties + task.jvmArgs += "-javaagent:$it" + } +} + +task sampleTest(type: Test) { + dependsOn cleanTest + include '**/*BStackSampleTest.*' + useJUnitPlatform() +} + +task sampleLocalTest(type: Test) { + dependsOn cleanTest + include '**/*BStackLocalTest.*' + useJUnitPlatform() +} diff --git a/junit-5/pom.xml b/junit-5/pom.xml index 1f9a868..871c15e 100644 --- a/junit-5/pom.xml +++ b/junit-5/pom.xml @@ -8,28 +8,26 @@ 8 8 - 5.8.1 - 5.4.0 + 5.9.1 1.1 - 4.1.0 + 4.1.4 3.0.0-M5 - 1.0.6 4.5.13 - 5 - **/tests.SingleTest.java - **/tests.LocalTest.java + 2.2 + **/tests.BStackSampleTest.java + **/tests.BStackLocalTest.java org.junit.jupiter - junit-jupiter-engine - ${junit-jupiter-engine} + junit-jupiter-api + ${junit-jupiter.version} test - org.junit.vintage - junit-vintage-engine - ${junit-vintage-engine} + org.junit.jupiter + junit-jupiter-engine + ${junit-jupiter.version} test @@ -47,35 +45,54 @@ maven-surefire-plugin ${maven-surefire-plugin} maven-plugin - - - ${tests.single} - - - parallel.conf.json - - - - junit.jupiter.execution.parallel.enabled = true - junit.jupiter.execution.parallel.mode.default = concurrent - junit.jupiter.execution.parallel.config.strategy=fixed - junit.jupiter.execution.parallel.config.fixed.parallelism=${parallel.count} - - - false - - - - com.browserstack - browserstack-local-java - ${browserstack-local-java} org.apache.httpcomponents httpclient ${httpclient} + + org.yaml + snakeyaml + ${snakeyaml.version} + + + com.browserstack + browserstack-java-sdk + LATEST + compile + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + getClasspathFilenames + + properties + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin} + + + ${tests.single} + + + -javaagent:${com.browserstack:browserstack-java-sdk:jar} + + false + + + + single @@ -89,9 +106,9 @@ ${tests.single} - - single.conf.json - + + -javaagent:${com.browserstack:browserstack-java-sdk:jar} + false @@ -110,38 +127,9 @@ ${tests.local} - - local.conf.json - - false - - - - - - - parallel - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.0.0-M5 - - - ${tests.single} - - - parallel.conf.json - - - - junit.jupiter.execution.parallel.enabled = true - junit.jupiter.execution.parallel.mode.default = concurrent - junit.jupiter.execution.parallel.config.strategy=fixed - junit.jupiter.execution.parallel.config.fixed.parallelism=${parallel.count} - - + + -javaagent:${com.browserstack:browserstack-java-sdk:jar} + false diff --git a/junit-5/settings.gradle b/junit-5/settings.gradle new file mode 100644 index 0000000..3e5b662 --- /dev/null +++ b/junit-5/settings.gradle @@ -0,0 +1,15 @@ +pluginManagement { + repositories { + mavenCentral() + mavenLocal() + gradlePluginPortal() + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "com.browserstack.gradle-sdk") { + useModule("com.browserstack:gradle-sdk:1.1.2") + } + } + } +} diff --git a/junit-5/src/test/java/runners/BstackRunner.java b/junit-5/src/test/java/runners/BstackRunner.java index ec98c51..a759228 100644 --- a/junit-5/src/test/java/runners/BstackRunner.java +++ b/junit-5/src/test/java/runners/BstackRunner.java @@ -1,140 +1,60 @@ package runners; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.junit.jupiter.api.extension.*; -import org.openqa.selenium.MutableCapabilities; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; -import utils.SetupLocalTesting; +import org.yaml.snakeyaml.Yaml; -import java.io.FileReader; -import java.net.MalformedURLException; +import java.io.File; +import java.io.InputStream; import java.net.URL; +import java.nio.file.Files; import java.util.*; -import java.util.stream.Stream; -public class BstackRunner implements TestTemplateInvocationContextProvider { +public class BstackRunner { public WebDriver driver; - public String username, accessKey, server; - private JSONObject mainConfig; - private JSONArray platformConfig; - private Map commonCapsConfig; + public static String userName, accessKey; + public static Map browserStackYamlMap; + public static final String USER_DIR = "user.dir"; public BstackRunner() { - this.username = setupCredsAndServer().get("username"); - this.accessKey = setupCredsAndServer().get("accesskey"); - this.server = setupCredsAndServer().get("server"); + File file = new File(getUserDir() + "/browserstack.yml"); + this.browserStackYamlMap = convertYamlFileToMap(file, new HashMap<>()); } - public HashMap setupCredsAndServer() { - try { - if (System.getProperty("config") != null) { - JSONParser parser = new JSONParser(); - mainConfig = (JSONObject) parser - .parse(new FileReader("src/test/resources/conf/" + System.getProperty("config"))); - } - server = (String) mainConfig.get("server"); - username = System.getenv("BROWSERSTACK_USERNAME"); - if (username == null) { - username = (String) mainConfig.get("userName"); - } - accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY"); - if (accessKey == null) { - accessKey = (String) mainConfig.get("accessKey"); - } - } catch (Exception e) { - System.out.println(e.getMessage()); - } - HashMap creds = new HashMap(); - creds.put("username", username); - creds.put("accesskey", accessKey); - creds.put("server", server); - return creds; + @BeforeEach + public void setUp() throws Exception { + MutableCapabilities capabilities = new MutableCapabilities(); + userName = System.getenv("BROWSERSTACK_USERNAME") != null ? System.getenv("BROWSERSTACK_USERNAME") : (String) browserStackYamlMap.get("userName"); + accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY") != null ? System.getenv("BROWSERSTACK_ACCESS_KEY") : (String) browserStackYamlMap.get("accessKey"); + HashMap bStackOptions = new HashMap<>(); + bStackOptions.put("source", "junit5:sample-master:v1.2"); + capabilities.setCapability("bstack:options", bStackOptions); + driver = new RemoteWebDriver( + new URL(String.format("https://%s:%s@hub.browserstack.com/wd/hub", userName , accessKey)), capabilities); } - @Override - public boolean supportsTestTemplate(ExtensionContext extensionContext) { - return true; + @AfterEach + public void tearDown() { + driver.quit(); } - @Override - public Stream provideTestTemplateInvocationContexts(ExtensionContext extensionContext) { - List desiredCapsInvocationContexts = new ArrayList<>(); + private String getUserDir() { + return System.getProperty(USER_DIR); + } + private Map convertYamlFileToMap(File yamlFile, Map map) { try { - platformConfig = (JSONArray) mainConfig.get("environments"); - commonCapsConfig = (Map) mainConfig.get("capabilities"); - - for (Object platform : platformConfig) { - MutableCapabilities capabilities = new MutableCapabilities(); - HashMap bstackOptions = new HashMap<>(); - Map envCapabilities = (Map) platform; - - Iterator> it = envCapabilities.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = it.next(); - if ("bstack:options".equals(pair.getKey())) { - bstackOptions.putAll((Map) pair.getValue()); - } else { - capabilities.setCapability(pair.getKey(), pair.getValue()); - } - } - - it = commonCapsConfig.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry pair = it.next(); - if ("bstack:options".equals(pair.getKey())) { - bstackOptions.putAll((Map) pair.getValue()); - } else { - capabilities.setCapability(pair.getKey(), pair.getValue()); - } - } - //Initializing local testing connection - if (bstackOptions.containsKey("local")) { - HashMap localOptions = new HashMap<>(); - localOptions.put("key", accessKey); - //Add more local options here, e.g. forceLocal, localIdentifier, etc. - SetupLocalTesting.createInstance(localOptions); - } - bstackOptions.put("source","junit-5:sample-master:v1.1"); - capabilities.setCapability("bstack:options", bstackOptions); - - desiredCapsInvocationContexts.add(invocationContext(capabilities)); - } + InputStream inputStream = Files.newInputStream(yamlFile.toPath()); + Yaml yaml = new Yaml(); + Map config = yaml.load(inputStream); + map.putAll(config); } catch (Exception e) { - e.printStackTrace(); + throw new RuntimeException(String.format("Malformed browserstack.yml file - %s.", e)); } - return desiredCapsInvocationContexts.stream(); - } - - private TestTemplateInvocationContext invocationContext(MutableCapabilities caps) { - return new TestTemplateInvocationContext() { - - @Override - public List getAdditionalExtensions() { - - return Collections.singletonList(new ParameterResolver() { - @Override - public boolean supportsParameter(ParameterContext parameterContext, - ExtensionContext extensionContext) { - return parameterContext.getParameter().getType().equals(WebDriver.class); - } - - @Override - public Object resolveParameter(ParameterContext parameterContext, - ExtensionContext extensionContext) { - try { - driver = new RemoteWebDriver(new URL("https://" + username + ":" + accessKey + "@" + server + "/wd/hub"), caps); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - return driver; - } - }); - } - }; + return map; } } diff --git a/junit-5/src/test/java/runners/WebDriverTest.java b/junit-5/src/test/java/runners/WebDriverTest.java deleted file mode 100644 index b68e0df..0000000 --- a/junit-5/src/test/java/runners/WebDriverTest.java +++ /dev/null @@ -1,18 +0,0 @@ -package runners; - -import org.apiguardian.api.API; -import org.junit.jupiter.api.TestTemplate; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.platform.commons.annotation.Testable; - -import java.lang.annotation.*; - -@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@API(status = API.Status.STABLE) -@TestTemplate -@Testable -@ExtendWith(BstackRunner.class) -public @interface WebDriverTest { -} diff --git a/junit-5/src/test/java/tests/BStackLocalTest.java b/junit-5/src/test/java/tests/BStackLocalTest.java new file mode 100644 index 0000000..75a4872 --- /dev/null +++ b/junit-5/src/test/java/tests/BStackLocalTest.java @@ -0,0 +1,16 @@ +package tests; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import runners.BstackRunner; + + +public class BStackLocalTest extends BstackRunner { + + @Test + void localTest() { + driver.get("http://bs-local.com:45454/"); + String validateContent = driver.getTitle(); + Assertions.assertTrue(validateContent.contains("BrowserStack Local"), "Local content not validated!"); + } +} diff --git a/junit-5/src/test/java/tests/BStackSampleTest.java b/junit-5/src/test/java/tests/BStackSampleTest.java new file mode 100644 index 0000000..74f55ce --- /dev/null +++ b/junit-5/src/test/java/tests/BStackSampleTest.java @@ -0,0 +1,27 @@ +package tests; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; +import runners.BstackRunner; + +import java.time.Duration; + +public class BStackSampleTest extends BstackRunner { + + @Test + void singleTest() { + driver.get("https://bstackdemo.com/"); + final WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); + wait.until(ExpectedConditions.titleIs("StackDemo")); + String product_name = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='1']/p"))).getText(); + WebElement cart_btn = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@id='1']/div[4]"))); + cart_btn.click(); + wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("float-cart__content"))); + final String product_in_cart = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='__next']/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]"))).getText(); + Assertions.assertTrue(product_name.matches(product_in_cart), "Product add to the cart - Failed!"); + } +} diff --git a/junit-5/src/test/java/tests/LocalTest.java b/junit-5/src/test/java/tests/LocalTest.java deleted file mode 100644 index de2d9bc..0000000 --- a/junit-5/src/test/java/tests/LocalTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package tests; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.remote.SessionId; -import runners.WebDriverTest; -import utils.MarkSessionStatus; - - -public class LocalTest { - - @WebDriverTest - void localTest(WebDriver driver) { - SessionId sessionId = ((RemoteWebDriver) driver).getSessionId(); - MarkSessionStatus sessionStatus = new MarkSessionStatus(sessionId); - - try { - driver.get("http://localhost:45691/check"); - String validateContent = driver.findElement(By.cssSelector("body")).getText(); - if (validateContent.contains("Up and running")) { - sessionStatus.markTestStatus("passed", "Local content validated!"); - } else { - sessionStatus.markTestStatus("failed", "Local content not validated!"); - } - } catch (Exception e) { - sessionStatus.markTestStatus("failed", "There was some issue!"); - System.out.println(e.getMessage()); - } - driver.quit(); - } -} diff --git a/junit-5/src/test/java/tests/SingleTest.java b/junit-5/src/test/java/tests/SingleTest.java deleted file mode 100644 index 9b2e95b..0000000 --- a/junit-5/src/test/java/tests/SingleTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package tests; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.remote.SessionId; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; -import runners.WebDriverTest; -import utils.MarkSessionStatus; - -import java.time.Duration; - -public class SingleTest { - - @WebDriverTest - void singleTest(WebDriver driver) { - SessionId sessionId = ((RemoteWebDriver) driver).getSessionId(); - MarkSessionStatus sessionStatus = new MarkSessionStatus(sessionId); - - try { - driver.get("https://bstackdemo.com/"); - final WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); - wait.until(ExpectedConditions.titleIs("StackDemo")); - String product_name = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='1']/p"))).getText(); - WebElement cart_btn = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@id='1']/div[4]"))); - cart_btn.click(); - wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(".float\\-cart__content"))); - final String product_in_cart = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='__next']/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]"))).getText(); - if (product_name.equals(product_in_cart)) { - sessionStatus.markTestStatus("passed", "Product has been successfully added to the cart!"); - } else { - sessionStatus.markTestStatus("failed", "There was some issue!"); - } - } catch (Exception e) { - sessionStatus.markTestStatus("failed", "There was some issue!"); - System.out.println("Exception: " + e.getMessage()); - } - driver.quit(); - } - - //@WebDriverTest - void bstackTest(WebDriver driver) { - driver.get("https://bstackdemo.com/"); - System.out.println("Test1: " + Thread.currentThread().getName()); - driver.quit(); - } -} diff --git a/junit-5/src/test/java/utils/MarkSessionStatus.java b/junit-5/src/test/java/utils/MarkSessionStatus.java deleted file mode 100644 index 923f020..0000000 --- a/junit-5/src/test/java/utils/MarkSessionStatus.java +++ /dev/null @@ -1,36 +0,0 @@ -package utils; - -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.message.BasicNameValuePair; -import org.openqa.selenium.remote.SessionId; -import runners.BstackRunner; - -import java.net.URI; -import java.util.ArrayList; - -public class MarkSessionStatus extends BstackRunner { - SessionId sessionId; - - public MarkSessionStatus(SessionId sessionId) { - this.sessionId = sessionId; - } - - public void markTestStatus(String status, String reason) { - try { - URI uri = new URI("https://" + username + ":" + accessKey + "@api.browserstack.com/automate/sessions/" + sessionId + ".json"); - HttpPut putRequest = new HttpPut(uri); - - ArrayList nameValuePairs = new ArrayList(); - nameValuePairs.add((new BasicNameValuePair("status", status))); - nameValuePairs.add((new BasicNameValuePair("reason", reason))); - putRequest.setEntity(new UrlEncodedFormEntity(nameValuePairs)); - - HttpClientBuilder.create().build().execute(putRequest); - } catch (Exception e) { - System.out.println("Exception: " + e.getMessage()); - } - } -} diff --git a/junit-5/src/test/java/utils/SetupLocalTesting.java b/junit-5/src/test/java/utils/SetupLocalTesting.java deleted file mode 100644 index 149d939..0000000 --- a/junit-5/src/test/java/utils/SetupLocalTesting.java +++ /dev/null @@ -1,53 +0,0 @@ -package utils; - -import com.browserstack.local.Local; - -import java.util.Map; - -public class SetupLocalTesting { - private static volatile SetupLocalTesting instance; - - private final Local local = new Local(); - - private SetupLocalTesting(Map localOptions) { - try { - local.start(localOptions); - } catch (Exception e) { - throw new RuntimeException("Initialization of BrowserStack Local failed.", e); - } - } - - public static void createInstance(Map args) { - if (instance == null) { - synchronized (SetupLocalTesting.class) { - if (instance == null) { - instance = new SetupLocalTesting(args); - Runtime.getRuntime().addShutdownHook(new Closer(instance.local)); - } - } - } - } - - public static SetupLocalTesting getInstance() { - return instance; - } - - private static class Closer extends Thread { - private final Local local; - - public Closer(Local local) { - this.local = local; - } - - @Override - public void run() { - try { - if (local.isRunning()) { - local.stop(); - } - } catch (Exception e) { - System.out.println("Error encountered while stopping BrowserStack Local { }" + e); - } - } - } -} diff --git a/junit-5/src/test/resources/conf/local.conf.json b/junit-5/src/test/resources/conf/local.conf.json deleted file mode 100644 index 6e02c8e..0000000 --- a/junit-5/src/test/resources/conf/local.conf.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "server": "hub.browserstack.com", - "userName": "BROWSERSTACK_USERNAME", - "accessKey": "BROWSERSTACK_ACCESS_KEY", - "capabilities": { - "bstack:options": { - "sessionName": "BStack local junit-5", - "buildName": "browserstack-build-1", - "projectName": "My Test Project", - "debug": true, - "local": true, - "seleniumVersion": "4.0.0" - } - }, - "environments": [ - { - "bstack:options": { - "os": "Windows", - "osVersion": "10" - }, - "browserName": "chrome", - "browserVersion": "latest-1" - } - ] -} diff --git a/junit-5/src/test/resources/conf/parallel.conf.json b/junit-5/src/test/resources/conf/parallel.conf.json deleted file mode 100644 index 14f0f8b..0000000 --- a/junit-5/src/test/resources/conf/parallel.conf.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "server": "hub.browserstack.com", - "userName": "BROWSERSTACK_USERNAME", - "accessKey": "BROWSERSTACK_ACCESS_KEY", - "capabilities": { - "bstack:options": { - "sessionName": "BStack parallel junit-5", - "buildName": "browserstack-build-1", - "projectName": "My Test Project", - "debug": true, - "seleniumVersion": "4.0.0" - } - }, - "environments": [ - { - "browserName": "chrome", - "bstack:options": { - "os": "OS X", - "osVersion": "Big Sur" - } - }, - { - "browserName": "safari", - "browserVersion": "latest", - "bstack:options": { - "os": "OS X", - "osVersion": "Monterey" - } - }, - { - "bstack:options": { - "deviceName": "iPhone 12" - } - } - ] -} diff --git a/junit-5/src/test/resources/conf/single.conf.json b/junit-5/src/test/resources/conf/single.conf.json deleted file mode 100644 index 6a807fb..0000000 --- a/junit-5/src/test/resources/conf/single.conf.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "server": "hub.browserstack.com", - "userName": "BROWSERSTACK_USERNAME", - "accessKey": "BROWSERSTACK_ACCESS_KEY", - "capabilities": { - "bstack:options": { - "sessionName": "BStack single junit-5", - "buildName": "browserstack-build-1", - "projectName": "My Test Project", - "seleniumVersion": "4.0.0", - "debug": true - } - }, - "environments": [ - { - "bstack:options": { - "os": "Windows", - "osVersion": "11" - }, - "browserName": "Chrome", - "browserVersion": "latest" - } - ] -}