-
Notifications
You must be signed in to change notification settings - Fork 0
Add reqnroll-nunit-appium App Automate sample (Android) #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
AakashHotchandani
wants to merge
7
commits into
main
Choose a base branch
from
sdk-sample
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
f68da52
Add reqnroll-nunit-appium App Automate sample (Android)
AakashHotchandani 4a172be
Restore scaffold files dropped by import (Semgrep CI workflow, CODEOW…
AakashHotchandani c32f8da
Add BrowserStack SDK sample-test GitHub Actions workflow (workflow_di…
AakashHotchandani a3c2d71
Add least-privilege permissions to sample-test workflow (CodeQL: limi…
AakashHotchandani 188f440
Address review (Sourav): commit public apps + relative app path (no a…
AakashHotchandani 7f7686f
Keep single-target net8.0 (consistent with the CI matrix); README sta…
AakashHotchandani ca8de2e
Sync C# target/CI with sibling: multi-target net6/8/9/10 + dotnet-ver…
AakashHotchandani File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # Runs the BrowserStack SDK sample against a given commit and reports a status check. | ||
| # Trigger: Actions tab -> "Reqnroll (NUnit) Appium App Automate SDK sample test" -> Run workflow -> paste the PR's full commit SHA. | ||
| # Requires repo secrets: BROWSERSTACK_USERNAME, BROWSERSTACK_ACCESS_KEY. | ||
| # NOTE (App Automate): the app under test is referenced via `app: bs://...` in browserstack.yml; | ||
| # ensure that uploaded app exists on the account whose secrets are used (re-upload + update if expired). | ||
| name: Reqnroll (NUnit) Appium App Automate SDK sample test | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| commit_sha: | ||
| description: 'The full commit id to build' | ||
| required: true | ||
|
|
||
| permissions: | ||
| contents: read # checkout | ||
| checks: write # github-script creates the status check | ||
|
|
||
| jobs: | ||
| sdk-sample: | ||
| runs-on: ${{ matrix.os }} | ||
| strategy: | ||
| fail-fast: false | ||
| max-parallel: 3 | ||
| matrix: | ||
| os: [windows-latest] | ||
| dotnet: ['6.0.x', '8.0.x', '9.0.x', '10.0.x'] | ||
| include: | ||
| - dotnet: '6.0.x' | ||
| target-framework: 'net6.0' | ||
| - dotnet: '8.0.x' | ||
| target-framework: 'net8.0' | ||
| - dotnet: '9.0.x' | ||
| target-framework: 'net9.0' | ||
| - dotnet: '10.0.x' | ||
| target-framework: 'net10.0' | ||
| name: reqnroll-nunit-appium .NET ${{ matrix.dotnet }} sample | ||
| env: | ||
| BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} | ||
| BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} | ||
| defaults: | ||
| run: | ||
| working-directory: android | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event.inputs.commit_sha }} | ||
| - name: Mark status check in_progress | ||
| uses: actions/github-script@v7 | ||
| env: | ||
| job_name: reqnroll-nunit-appium .NET ${{ matrix.dotnet }} sample | ||
| commit_sha: ${{ github.event.inputs.commit_sha }} | ||
| with: | ||
| github-token: ${{ github.token }} | ||
| script: | | ||
| 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(e => console.log('check create failed:', e.status)); | ||
| - uses: actions/setup-dotnet@v4 | ||
| with: | ||
| dotnet-version: ${{ matrix.dotnet }} | ||
| - name: Restore | ||
| run: | | ||
| dotnet tool restore | ||
| dotnet restore | ||
| - name: Run sample test | ||
| run: | | ||
| dotnet test --framework ${{ matrix.target-framework }} --filter "TestCategory=sample-test" | ||
| - name: Mark status check completed | ||
| if: always() | ||
| uses: actions/github-script@v7 | ||
| env: | ||
| conclusion: ${{ job.status }} | ||
| job_name: reqnroll-nunit-appium .NET ${{ matrix.dotnet }} sample | ||
| commit_sha: ${{ github.event.inputs.commit_sha }} | ||
| with: | ||
| github-token: ${{ github.token }} | ||
| script: | | ||
| 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(e => console.log('check create failed:', e.status)); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # .NET build output | ||
| bin/ | ||
| obj/ | ||
| *.user | ||
|
|
||
| # Reqnroll / test artifacts | ||
| TestResults/ | ||
| *.feature.cs | ||
|
|
||
| # BrowserStack SDK logs | ||
| log/ | ||
| local.log | ||
| *.log | ||
|
|
||
| # IDE | ||
| .vs/ | ||
| .vscode/ | ||
| .idea/ | ||
|
|
||
| # Credentials (never commit) | ||
| .bstack.env |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,74 @@ | ||
| # reqnroll-nunit-appium-app-browserstack | ||
| We require the following new public repositories under the browserstack GitHub organization to host customer-facing sample projects for the BrowserStack SDK. | ||
| # Reqnroll (NUnit) + Appium with BrowserStack App Automate | ||
|
|
||
| Run native Android app tests on real devices on [BrowserStack App Automate](https://app-automate.browserstack.com/) | ||
| using [Reqnroll](https://reqnroll.net/) (BDD) on top of NUnit + the Appium .NET client, integrated through the | ||
| [BrowserStack C# SDK](https://www.nuget.org/packages/BrowserStack.TestAdapter) (`BrowserStack.TestAdapter`). | ||
|
|
||
| The SDK reads `browserstack.yml`, uploads/uses the app, provisions the device, and routes the Appium session | ||
| to the BrowserStack cloud — no changes to your test logic are required. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - A [BrowserStack account](https://www.browserstack.com/users/sign_up) (username + access key). | ||
| - [.NET SDK](https://dotnet.microsoft.com/download) 8.0 (this sample targets `net8.0`; the CI workflow tests on 8.0.x). | ||
| - The sample app is the public **WikipediaSample.apk**, committed in `android/` and referenced by relative | ||
| path (`app: ./WikipediaSample.apk`) — the SDK uploads it at run time, so the sample works on any account. | ||
| To use your own build, replace the `app:` value (or set `BROWSERSTACK_APP`). | ||
|
|
||
| ## Setup | ||
|
|
||
| ```bash | ||
| git clone <this-repo> | ||
| cd reqnroll-nunit-appium-app-browserstack | ||
|
|
||
| # Configure credentials (either edit browserstack.yml or export env vars) | ||
| export BROWSERSTACK_USERNAME="YOUR_USERNAME" | ||
| export BROWSERSTACK_ACCESS_KEY="YOUR_ACCESS_KEY" | ||
| ``` | ||
|
|
||
| Credentials can be set in `android/browserstack.yml` (`userName` / `accessKey`) or via the | ||
| `BROWSERSTACK_USERNAME` / `BROWSERSTACK_ACCESS_KEY` environment variables (env vars take precedence). | ||
|
|
||
| This is an App Automate (mobile) sample, so the Android platform lives in its own self-contained directory | ||
| (`android/`) with its own `browserstack.yml`. | ||
|
|
||
| ## Run Sample Test | ||
|
|
||
| The sample test drives **WikipediaSample.apk**: it taps "Search Wikipedia", types "BrowserStack", and asserts | ||
| that search results are listed. | ||
|
|
||
| ```bash | ||
| cd android | ||
| dotnet restore | ||
| dotnet test --filter "TestCategory=sample-test" | ||
| ``` | ||
|
|
||
| To run every scenario in the project: | ||
|
|
||
| ```bash | ||
| cd android | ||
| dotnet test | ||
| ``` | ||
|
|
||
| ## Run Local Test | ||
|
|
||
| The local test drives **LocalSample.apk** to prove the BrowserStack Local tunnel is connected. It is tagged | ||
| `@sample-local-test` and is skipped by default in the sample run above. To run it, point `app:` in | ||
| `android/browserstack.yml` at a pre-uploaded `LocalSample.apk`, set `browserstackLocal: true`, then: | ||
|
|
||
| ```bash | ||
| cd android | ||
| dotnet test --filter "TestCategory=sample-local-test" | ||
| ``` | ||
|
|
||
| The BrowserStack SDK starts and manages the Local tunnel automatically when `browserstackLocal: true` is set | ||
| in `browserstack.yml`. | ||
|
|
||
| ## Notes / Dashboard | ||
|
|
||
| - View runs, video, device logs, and network logs on the | ||
| [App Automate dashboard](https://app-automate.browserstack.com/). | ||
| - `testObservability: true` also surfaces the build on | ||
| [Test Observability](https://observability.browserstack.com/). | ||
| - The driver is created with an empty `AppiumOptions` object; all capabilities (app, device, `bstack:options`) | ||
| are injected by the SDK from `browserstack.yml`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| { | ||
| "version": 1, | ||
| "isRoot": true, | ||
| "tools": {} | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| @sample-local-test | ||
| Feature: BrowserStack Local Testing (Local Sample App) | ||
| Scenario: Verify the BrowserStack Local tunnel from the device | ||
| Given I start the test on the Local Sample App | ||
| Then I should see the connection is up and running |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| @sample-test | ||
| Feature: BrowserStack Sample (Wikipedia App) | ||
| Scenario: Search Wikipedia for BrowserStack | ||
| Given I try to search using the Wikipedia App | ||
| When I search with the keyword BrowserStack | ||
| Then the search results should be listed |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| using System; | ||
| using System.Threading; | ||
| using Reqnroll; | ||
| using OpenQA.Selenium.Appium; | ||
| using OpenQA.Selenium.Appium.Android; | ||
|
|
||
| namespace ReqnrollAppiumBrowserStack | ||
| { | ||
| // Manages the Appium Android driver lifecycle for each scenario. | ||
| // | ||
| // The driver is created with an EMPTY AppiumOptions object against the | ||
| // BrowserStack hub. The BrowserStack SDK (BrowserStack.TestAdapter) | ||
| // intercepts the Appium driver construction and injects the app, device, | ||
| // and bstack:options capabilities from android/browserstack.yml — no | ||
| // capabilities are hardcoded here. | ||
| [Binding] | ||
| public class AppiumHooks | ||
| { | ||
| // Shared with the step definitions for the current scenario. | ||
| public static ThreadLocal<AndroidDriver<AndroidElement>> ThreadLocalDriver = | ||
| new ThreadLocal<AndroidDriver<AndroidElement>>(); | ||
|
|
||
| [BeforeScenario(Order = 0)] | ||
| public static void Initialize() | ||
| { | ||
| AppiumOptions appiumOptions = new AppiumOptions(); | ||
| ThreadLocalDriver.Value = new AndroidDriver<AndroidElement>( | ||
| new Uri("https://hub-cloud.browserstack.com/wd/hub/"), appiumOptions); | ||
| } | ||
|
|
||
| [AfterScenario] | ||
| public static void TearDown() | ||
| { | ||
| if (ThreadLocalDriver.IsValueCreated) | ||
| { | ||
| ThreadLocalDriver.Value?.Quit(); | ||
| } | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| using System; | ||
| using System.Threading; | ||
| using System.Collections.ObjectModel; | ||
| using Reqnroll; | ||
| using NUnit.Framework; | ||
| using OpenQA.Selenium; | ||
| using OpenQA.Selenium.Appium.Android; | ||
| using OpenQA.Selenium.Support.UI; | ||
|
|
||
| namespace ReqnrollAppiumBrowserStack | ||
| { | ||
| [Binding] | ||
| public class SampleLocalTestSteps | ||
| { | ||
| private readonly AndroidDriver<AndroidElement> _driver; | ||
|
|
||
| public SampleLocalTestSteps() | ||
| { | ||
| _driver = AppiumHooks.ThreadLocalDriver.Value!; | ||
| } | ||
|
|
||
| [Given(@"I start the test on the Local Sample App")] | ||
| public void GivenIStartTestOnLocalApp() | ||
| { | ||
| AndroidElement testAction = (AndroidElement)new WebDriverWait(_driver, TimeSpan.FromSeconds(30)) | ||
| .Until(ExpectedConditions.ElementToBeClickable( | ||
| By.Id("com.example.android.basicnetworking:id/test_action"))); | ||
| testAction.Click(); | ||
| } | ||
|
|
||
| [Then(@"I should see the connection is up and running")] | ||
| public void ThenIShouldSeeUpAndRunning() | ||
| { | ||
| Thread.Sleep(5000); | ||
| ReadOnlyCollection<AndroidElement> textViews = | ||
| _driver.FindElements(By.ClassName("android.widget.TextView")); | ||
|
|
||
| bool found = false; | ||
| foreach (AndroidElement element in textViews) | ||
| { | ||
| if (element.Text != null && element.Text.Contains("The active connection is")) | ||
| { | ||
| found = true; | ||
| break; | ||
| } | ||
| } | ||
| Assert.That(found, Is.True, "Expected the Local Sample App to report an active connection"); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| using System; | ||
| using System.Threading; | ||
| using System.Collections.ObjectModel; | ||
| using Reqnroll; | ||
| using NUnit.Framework; | ||
| using OpenQA.Selenium; | ||
| using OpenQA.Selenium.Appium; | ||
| using OpenQA.Selenium.Appium.Android; | ||
| using OpenQA.Selenium.Support.UI; | ||
|
|
||
| namespace ReqnrollAppiumBrowserStack | ||
| { | ||
| [Binding] | ||
| public class SampleTestSteps | ||
| { | ||
| private readonly AndroidDriver<AndroidElement> _driver; | ||
|
|
||
| public SampleTestSteps() | ||
| { | ||
| _driver = AppiumHooks.ThreadLocalDriver.Value!; | ||
| } | ||
|
|
||
| [Given(@"I try to search using the Wikipedia App")] | ||
| public void GivenITrySearchWikipediaApp() | ||
| { | ||
| AndroidElement searchElement = (AndroidElement)new WebDriverWait(_driver, TimeSpan.FromSeconds(30)) | ||
| .Until(ExpectedConditions.ElementToBeClickable( | ||
| MobileBy.AccessibilityId("Search Wikipedia"))); | ||
| searchElement.Click(); | ||
| } | ||
|
|
||
| [When(@"I search with the keyword BrowserStack")] | ||
| public void WhenISearchKeywordBrowserStack() | ||
| { | ||
| AndroidElement insertTextElement = (AndroidElement)new WebDriverWait(_driver, TimeSpan.FromSeconds(30)) | ||
| .Until(ExpectedConditions.ElementToBeClickable( | ||
| By.Id("org.wikipedia.alpha:id/search_src_text"))); | ||
| insertTextElement.SendKeys("BrowserStack"); | ||
| Thread.Sleep(5000); | ||
| } | ||
|
|
||
| [Then(@"the search results should be listed")] | ||
| public void ThenSearchResultsShouldBeListed() | ||
| { | ||
| ReadOnlyCollection<AndroidElement> results = | ||
| _driver.FindElements(By.ClassName("android.widget.TextView")); | ||
| Assert.That(results.Count, Is.GreaterThan(0)); | ||
| } | ||
| } | ||
| } |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFrameworks>net6.0;net8.0;net9.0;net10.0</TargetFrameworks> | ||
| <RootNamespace>ReqnrollAppiumBrowserStack</RootNamespace> | ||
| <AssemblyName>ReqnrollAppiumBrowserStack</AssemblyName> | ||
| <GenerateAssemblyInfo>false</GenerateAssemblyInfo> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <!-- BrowserStack SDK: instruments the test runner and routes the Appium | ||
| session to the BrowserStack cloud using android/browserstack.yml. --> | ||
| <PackageReference Include="BrowserStack.TestAdapter" Version="0.*" /> | ||
|
|
||
| <!-- Test + BDD frameworks --> | ||
| <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" /> | ||
| <PackageReference Include="Reqnroll.NUnit" Version="3.2.1" /> | ||
| <PackageReference Include="NUnit" Version="3.13.3" /> | ||
| <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" /> | ||
|
|
||
| <!-- Appium client for Android automation --> | ||
| <PackageReference Include="Appium.WebDriver" Version="4.4.0" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.