├── .gitattributes
├── .github
├── CODEOWNERS
└── workflows
│ ├── Semgrep.yml
│ └── reviewing_changes.yml
├── .gitignore
├── SpecFlow_BrowserStack.sln
├── SpecFlow_BrowserStack
├── Properties
│ └── AssemblyInfo.cs
├── SpecFlow_BrowserStack.csproj
├── browserstack.yml
└── resources
│ ├── features
│ ├── SampleLocalTest.feature
│ └── SampleTest.feature
│ └── src
│ ├── BrowserStackSpecFlowTest.cs
│ ├── SampleLocalTest.cs
│ └── SampleTest.cs
└── readme.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | .github/* @browserstack/asi-devs
2 |
3 | * @browserstack/automate-public-repos
4 |
--------------------------------------------------------------------------------
/.github/workflows/Semgrep.yml:
--------------------------------------------------------------------------------
1 | # Name of this GitHub Actions workflow.
2 | name: Semgrep
3 |
4 | on:
5 | # Scan changed files in PRs (diff-aware scanning):
6 | # The branches below must be a subset of the branches above
7 | pull_request:
8 | branches: ["master", "main"]
9 | push:
10 | branches: ["master", "main"]
11 | schedule:
12 | - cron: '0 6 * * *'
13 |
14 |
15 | permissions:
16 | contents: read
17 |
18 | jobs:
19 | semgrep:
20 | # User definable name of this GitHub Actions job.
21 | permissions:
22 | contents: read # for actions/checkout to fetch code
23 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
24 | name: semgrep/ci
25 | # If you are self-hosting, change the following `runs-on` value:
26 | runs-on: ubuntu-latest
27 |
28 | container:
29 | # A Docker image with Semgrep installed. Do not change this.
30 | image: returntocorp/semgrep
31 |
32 | # Skip any PR created by dependabot to avoid permission issues:
33 | if: (github.actor != 'dependabot[bot]')
34 |
35 | steps:
36 | # Fetch project source with GitHub Actions Checkout.
37 | - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
38 | # Run the "semgrep ci" command on the command line of the docker image.
39 | - run: semgrep ci --sarif --output=semgrep.sarif
40 | env:
41 | # Add the rules that Semgrep uses by setting the SEMGREP_RULES environment variable.
42 | SEMGREP_RULES: p/default # more at semgrep.dev/explore
43 |
44 | - name: Upload SARIF file for GitHub Advanced Security Dashboard
45 | uses: github/codeql-action/upload-sarif@6c089f53dd51dc3fc7e599c3cb5356453a52ca9e # v2.20.0
46 | with:
47 | sarif_file: semgrep.sarif
48 | if: always()
--------------------------------------------------------------------------------
/.github/workflows/reviewing_changes.yml:
--------------------------------------------------------------------------------
1 | # This job is to test different profiles in sdk branch against Pull Requests raised
2 | # This workflow targets specflow
3 |
4 | name: C-sharp SDK Test workflow on workflow_dispatch
5 |
6 | on:
7 | workflow_dispatch:
8 | inputs:
9 | commit_sha:
10 | description: 'The full commit id to build'
11 | required: true
12 |
13 | jobs:
14 | comment-run:
15 | runs-on: ${{ matrix.os }}
16 | strategy:
17 | fail-fast: false
18 | max-parallel: 3
19 | matrix:
20 | dotnet: ['6.0.x', '5.0.x']
21 | os: [ windows-latest ]
22 | name: SpecFlow Repo ${{ matrix.dotnet }} - ${{ matrix.os }} Sample
23 | env:
24 | BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
25 | BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
26 |
27 | steps:
28 | - uses: actions/checkout@v3
29 | with:
30 | ref: ${{ github.event.inputs.commit_sha }}
31 | - uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975
32 | id: status-check-in-progress
33 | env:
34 | job_name: SpecFlow Repo ${{ matrix.dotnet }} - ${{ matrix.os }} Sample
35 | commit_sha: ${{ github.event.inputs.commit_sha }}
36 | with:
37 | github-token: ${{ github.token }}
38 | script: |
39 | const result = await github.rest.checks.create({
40 | owner: context.repo.owner,
41 | repo: context.repo.repo,
42 | name: process.env.job_name,
43 | head_sha: process.env.commit_sha,
44 | status: 'in_progress'
45 | }).catch((err) => ({status: err.status, response: err.response}));
46 | console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`)
47 | if (result.status !== 201) {
48 | console.log('Failed to create check run')
49 | }
50 | - name: Setup dotnet
51 | uses: actions/setup-dotnet@v3
52 | with:
53 | dotnet-version: ${{ matrix.dotnet }}
54 |
55 | - name: Install dependencies
56 | run: dotnet build
57 |
58 | - name: Run sample tests
59 | run: dotnet test --filter "Category=sample-test"
60 |
61 | - name: Run local tests
62 | run: dotnet test --filter "Category=sample-local-test"
63 |
64 | - if: always()
65 | uses: actions/github-script@98814c53be79b1d30f795b907e553d8679345975
66 | id: status-check-completed
67 | env:
68 | conclusion: ${{ job.status }}
69 | job_name: SpecFlow Repo ${{ matrix.dotnet }} - ${{ matrix.os }} Sample
70 | commit_sha: ${{ github.event.inputs.commit_sha }}
71 | with:
72 | github-token: ${{ github.token }}
73 | script: |
74 | const result = await github.rest.checks.create({
75 | owner: context.repo.owner,
76 | repo: context.repo.repo,
77 | name: process.env.job_name,
78 | head_sha: process.env.commit_sha,
79 | status: 'completed',
80 | conclusion: process.env.conclusion
81 | }).catch((err) => ({status: err.status, response: err.response}));
82 | console.log(`The status-check response : ${result.status} Response : ${JSON.stringify(result.response)}`)
83 | if (result.status !== 201) {
84 | console.log('Failed to create check run')
85 | }
86 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | packages/
2 | bin
3 | obj
4 | *.user
5 | *.suo
6 | *.html
7 | *.txt
8 | *.xml
9 | *.features.cs
10 | .DS_Store
11 | *.log
12 | .vs
13 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2042
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{925E0612-77F8-459D-8100-87AFA52B581A}"
7 | ProjectSection(SolutionItems) = preProject
8 | build.ps1 = build.ps1
9 | build_local.ps1 = build_local.ps1
10 | EndProjectSection
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpecFlow_BrowserStack", "SpecFlow_BrowserStack\SpecFlow_BrowserStack.csproj", "{445D58CF-ED98-4C47-9E51-7EEF066305FF}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {445D58CF-ED98-4C47-9E51-7EEF066305FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {445D58CF-ED98-4C47-9E51-7EEF066305FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {445D58CF-ED98-4C47-9E51-7EEF066305FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {445D58CF-ED98-4C47-9E51-7EEF066305FF}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {3AA72975-DF48-47E1-9152-D2087415A1B5}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 |
3 | // For more details on LevelOfParallelism Attribute review NUnit documentation- https://github.com/nunit/docs/wiki/LevelOfParallelism-Attribute
4 | [assembly: LevelOfParallelism(5)]
5 | [assembly: Parallelizable(ParallelScope.Fixtures)]
6 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack/SpecFlow_BrowserStack.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | false
6 | enable
7 | enable
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack/browserstack.yml:
--------------------------------------------------------------------------------
1 | # =============================
2 | # Set BrowserStack Credentials
3 | # =============================
4 | # Add your BrowserStack userName and acccessKey here or set BROWSERSTACK_USERNAME and
5 | # BROWSERSTACK_ACCESS_KEY as env variables
6 |
7 | userName: YOUR_USERNAME
8 | accessKey: YOUR_ACCESS_KEY
9 |
10 | # ======================
11 | # BrowserStack Reporting
12 | # ======================
13 | # The following capabilities are used to set up reporting on BrowserStack:
14 | # Set 'projectName' to the name of your project. Example, Marketing Website
15 | projectName: BrowserStack Samples
16 | # Set `buildName` as the name of the job / testsuite being run
17 | buildName: browserstack build
18 | # `buildIdentifier` is a unique id to differentiate every execution that gets appended to
19 | # buildName. Choose your buildIdentifier format from the available expressions:
20 | # ${BUILD_NUMBER} (Default): Generates an incremental counter with every execution
21 | # ${DATE_TIME}: Generates a Timestamp with every execution. Eg. 05-Nov-19:30
22 | # Read more about buildIdentifiers here -> https://www.browserstack.com/docs/automate/selenium/organize-tests
23 | buildIdentifier: '#${BUILD_NUMBER}' # Supports strings along with either/both ${expression}
24 | # Set `source` in the syntax `:sample-:
25 |
26 | source: specflow:sample-sdk:v1.0
27 |
28 | # =======================================
29 | # Platforms (Browsers / Devices to test)
30 | # =======================================
31 | # Platforms object contains all the browser / device combinations you want to test on.
32 | # Entire list available here -> (https://www.browserstack.com/list-of-browsers-and-platforms/automate)
33 | platforms:
34 | - os: OS X
35 | osVersion: Big Sur
36 | browserName: Chrome
37 | browserVersion: latest
38 | - os: Windows
39 | osVersion: 10
40 | browserName: Edge
41 | browserVersion: latest
42 | - deviceName: Samsung Galaxy S22 Ultra
43 | browserName: chrome # Try 'samsung' for Samsung browser
44 | osVersion: 12.0
45 |
46 | # ==========================================
47 | # BrowserStack Local
48 | # (For localhost, staging/private websites)
49 | # ==========================================
50 | # Set browserStackLocal to true if your website under test is not accessible publicly over the internet
51 | # Learn more about how BrowserStack Local works here -> https://www.browserstack.com/docs/automate/selenium/local-testing-introduction
52 | browserstackLocal: true # (Default false)
53 |
54 | # browserStackLocalOptions:
55 | # Options to be passed to BrowserStack local in-case of advanced configurations
56 | # localIdentifier: # (Default: null) Needed if you need to run multiple instances of local.
57 | # forceLocal: true # (Default: false) Set to true if you need to resolve all your traffic via BrowserStack Local tunnel.
58 | # Entire list of arguments availabe here -> https://www.browserstack.com/docs/automate/selenium/manage-incoming-connections
59 |
60 | # ===================
61 | # Debugging features
62 | # ===================
63 | debug: false # # Set to true if you need screenshots for every selenium command ran
64 | networkLogs: false # Set to true to enable HAR logs capturing
65 | consoleLogs: errors # Remote browser's console debug levels to be printed (Default: errors)
66 | # Available options are `disable`, `errors`, `warnings`, `info`, `verbose` (Default: errors)
67 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack/resources/features/SampleLocalTest.feature:
--------------------------------------------------------------------------------
1 | @sample-local-test
2 | Feature: BStack Local
3 | Scenario Outline: Open BrowserStack Local
4 | Given I navigate to local website
5 | Then title should contain BrowserStack Local
6 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack/resources/features/SampleTest.feature:
--------------------------------------------------------------------------------
1 | @sample-test
2 | Feature: BStack Sample
3 | Scenario Outline: Can add product to cart
4 | Given I navigate to website
5 | Then I should see title StackDemo
6 | Then I add product to cart
7 | When I check if cart is opened
8 | Then I should see same product in cart
9 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack/resources/src/BrowserStackSpecFlowTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using TechTalk.SpecFlow;
3 | using log4net;
4 | using OpenQA.Selenium;
5 | using OpenQA.Selenium.Remote;
6 | using OpenQA.Selenium.Chrome;
7 | using System.Threading;
8 |
9 | namespace SpecFlowBrowserStack
10 | {
11 | [Binding]
12 | public class BrowserStackSpecFlowTest
13 | {
14 | private FeatureContext _featureContext;
15 | private ScenarioContext _scenarioContext;
16 |
17 | public static ThreadLocal ThreadLocalDriver = new ThreadLocal();
18 | private static readonly ILog log = LogManager.GetLogger(typeof(BrowserStackSpecFlowTest));
19 |
20 | public BrowserStackSpecFlowTest(FeatureContext featureContext, ScenarioContext scenarioContext)
21 | {
22 | _featureContext = featureContext;
23 | _scenarioContext = scenarioContext;
24 | }
25 |
26 |
27 | [BeforeScenario]
28 | public static void Initialize(ScenarioContext scenarioContext)
29 | {
30 | ChromeOptions capabilities = new ChromeOptions();
31 | ThreadLocalDriver.Value = new RemoteWebDriver(new Uri("https://hub.browserstack.com/wd/hub/"),capabilities);
32 | }
33 |
34 |
35 | [AfterScenario]
36 | public static void TearDown(ScenarioContext scenarioContext)
37 | {
38 | Shutdown();
39 | }
40 |
41 | protected static void Shutdown()
42 | {
43 | if (ThreadLocalDriver.IsValueCreated)
44 | {
45 | ThreadLocalDriver.Value?.Quit();
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack/resources/src/SampleLocalTest.cs:
--------------------------------------------------------------------------------
1 | using TechTalk.SpecFlow;
2 | using OpenQA.Selenium;
3 | using System;
4 | using OpenQA.Selenium.Support.UI;
5 | using NUnit.Framework;
6 | using SeleniumExtras.WaitHelpers;
7 | using System.Threading;
8 |
9 | namespace SpecFlowBrowserStack
10 | {
11 | [Binding]
12 | public class SampleLocalTest
13 | {
14 | private readonly IWebDriver _driver;
15 |
16 | public SampleLocalTest()
17 | {
18 | _driver = BrowserStackSpecFlowTest.ThreadLocalDriver.Value;
19 | new WebDriverWait(_driver, TimeSpan.FromSeconds(30));
20 | }
21 |
22 | [Given(@"I navigate to local website")]
23 | public void GivenINavigatedTowebsite()
24 | {
25 | _driver.Navigate().GoToUrl("http://bs-local.com:45454/");
26 | }
27 |
28 | [Then(@"title should contain (.*)")]
29 | public void ThenIShouldSeeTitle(string localString)
30 | {
31 | Thread.Sleep(5000);
32 | StringAssert.Contains(localString, _driver.Title);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/SpecFlow_BrowserStack/resources/src/SampleTest.cs:
--------------------------------------------------------------------------------
1 | using TechTalk.SpecFlow;
2 | using OpenQA.Selenium;
3 | using System;
4 | using OpenQA.Selenium.Support.UI;
5 | using NUnit.Framework;
6 | using SeleniumExtras.WaitHelpers;
7 | using System.Threading;
8 |
9 | namespace SpecFlowBrowserStack
10 | {
11 | [Binding]
12 | public class SampleTest
13 |
14 | {
15 | private readonly IWebDriver _driver;
16 | private string? productOnPageText;
17 | private string? productOnCartText;
18 | private bool? cartOpened;
19 | readonly WebDriverWait wait;
20 |
21 | public SampleTest()
22 | {
23 | _driver = BrowserStackSpecFlowTest.ThreadLocalDriver.Value;
24 | wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(30));
25 | }
26 |
27 | [Given(@"I navigate to website")]
28 | public void GivenINavigatedTowebsite()
29 | {
30 | _driver.Navigate().GoToUrl("https://bstackdemo.com");
31 | }
32 |
33 | [Then(@"I should see title (.*)")]
34 | public void ThenIShouldSeeTitle(string title)
35 | {
36 | Thread.Sleep(5000);
37 | Assert.That(_driver.Title, Is.EqualTo(title));
38 | }
39 |
40 | [Then(@"I add product to cart")]
41 | public void ThenIAddProductToCart()
42 | {
43 | wait.Until(ExpectedConditions.ElementExists(By.XPath("//*[@id=\"1\"]/p")));
44 | productOnPageText = _driver.FindElement(By.XPath("//*[@id=\"1\"]/p")).Text;
45 | wait.Until(ExpectedConditions.ElementExists(By.XPath("//*[@id=\"1\"]/div[4]")));
46 | _driver.FindElement(By.XPath("//*[@id=\"1\"]/div[4]")).Click();
47 | }
48 |
49 | [When(@"I check if cart is opened")]
50 | public void ThenICheckIfCartIsOpened()
51 | {
52 | cartOpened = _driver.FindElement(By.XPath("//*[@class=\"float-cart__content\"]")).Displayed;
53 | Assert.That(cartOpened, Is.EqualTo(true));
54 | }
55 |
56 | [Then(@"I should see same product in cart")]
57 | public void ThenIShouldSeeSameProductInCart()
58 | {
59 | productOnCartText = _driver.FindElement(By.XPath("//*[@id=\"__next\"]/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]")).Text;
60 | Assert.That(productOnCartText, Is.EqualTo(productOnPageText));
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # specflow-browserstack
2 | [SpecFlow](http://www.specflow.org/) Integration with BrowserStack.
3 |
4 | 
5 |
6 |
7 |
8 | ## Setup
9 | * Clone the repo
10 | * Open the solution `SpecFlow_BrowserStack.sln` in Visual Studio.
11 | * Build the solution
12 | * Update `browserstack.yml` file with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings)
13 |
14 | ### Running your tests from CLI
15 | * To run the test suite having cross-platform with parallelization, dotnet test --filter "Category=sample-test"
16 | * To run local tests, dotnet test --filter "Category=sample-local-test"
17 | ### Running your tests from Test Explorer
18 | - To run a parallel tests, run test with fixture `sample-test`
19 | - To run local tests, run test with fixture `sample-local-test`
20 |
21 | Understand how many parallel sessions you need by using our [Parallel Test Calculator](https://www.browserstack.com/automate/parallel-calculator?ref=github)
22 |
23 | ## Integrate your test suite
24 |
25 | This repository uses the BrowserStack SDK to run tests on BrowserStack. Follow the steps below to install the SDK in your test suite and run tests on BrowserStack:
26 |
27 | * Create sample browserstack.yml file with the browserstack related capabilities with your [BrowserStack Username and Access Key](https://www.browserstack.com/accounts/settings) and place it in your root folder.
28 | * Add nuget library BrowserStack.TestAdapter
29 | ```sh
30 | dotnet add BrowserStack.TestAdapter
31 | ```
32 | * Build project `dotnet build`
33 |
34 | ## Notes
35 | * You can view your test results on the [BrowserStack automate dashboard](https://www.browserstack.com/automate)
36 | * To test on a different set of browsers, check out our [platform configurator](https://www.browserstack.com/automate/c-sharp#setting-os-and-browser)
37 | * You can export the environment variables for the Username and Access Key of your BrowserStack account
38 |
39 | ```
40 | export BROWSERSTACK_USERNAME= &&
41 | export BROWSERSTACK_ACCESS_KEY=
42 | ```
43 |
44 | ## Additional Resources
45 | * [Documentation for writing automate test scripts in C#](https://www.browserstack.com/automate/c-sharp)
46 | * [Customizing your tests on BrowserStack](https://www.browserstack.com/automate/capabilities)
47 | * [Browsers & mobile devices for selenium testing on BrowserStack](https://www.browserstack.com/list-of-browsers-and-platforms?product=automate)
48 | * [Using REST API to access information about your tests via the command-line interface](https://www.browserstack.com/automate/rest-api)
49 |
--------------------------------------------------------------------------------