Skip to content

Commit

Permalink
feat: Initial working Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Zemotacqy committed Apr 30, 2024
1 parent 96932ca commit 0cbdf17
Show file tree
Hide file tree
Showing 23 changed files with 1,132 additions and 2 deletions.
405 changes: 405 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions Android/Android.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
<PackageReference Include="BrowserStackLocal" Version="2.3.1" />
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
<PackageReference Include="DotNetSeleniumExtras.WaitHelpers" Version="3.11.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
<ItemGroup>
<None Remove="Config\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Config\" />
</ItemGroup>
</Project>
15 changes: 15 additions & 0 deletions Android/Config/LocalTest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"platformName": "android",
"appium:deviceName": "Google Pixel 3",
"appium:osVersion": "9.0",
"appium:app": "bs://<app_hashed_id>",
"bstack:options": {
"projectName": "BStack Demo",
"buildName": "browserstack-build-1",
"sessionName": "BStack local mstest",
"userName": "BROWSERSTACK_USERNAME",
"accessKey": "BROWSERSTACK_ACCESS_KEY",
"debug": true,
"local": true
}
}
27 changes: 27 additions & 0 deletions Android/Config/ParallelTest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"commonCaps": {
"platformName": "android",
"appium:app": "bs://<app_hashed_id>",
"bstack:options": {
"projectName": "BStack Demo",
"buildName": "browserstack-build-1",
"userName": "BROWSERSTACK_USERNAME",
"accessKey": "BROWSERSTACK_ACCESS_KEY",
"debug": true
}
},
"pixel3": {
"appium:deviceName": "Google Pixel 3",
"appium:osVersion": "9.0",
"bstack:options": {
"sessionName": "BStack parallel mstest"
}
},
"samsungs10": {
"appium:deviceName": "Samsung Galaxy S10",
"appium:osVersion": "9.0",
"bstack:options": {
"sessionName": "BStack parallel mstest"
}
}
}
14 changes: 14 additions & 0 deletions Android/Config/SingleTest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"platformName": "android",
"appium:deviceName": "Google Pixel 3",
"appium:osVersion": "9.0",
"appium:app": "bs://<app_hashed_id>",
"bstack:options": {
"projectName": "BStack Demo",
"buildName": "browserstack-build-1",
"sessionName": "BStack single mstest",
"userName": "BROWSERSTACK_USERNAME",
"accessKey": "BROWSERSTACK_ACCESS_KEY",
"debug": true
}
}
64 changes: 64 additions & 0 deletions Android/LocalTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using BrowserStack;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System.Collections.ObjectModel;

namespace Android;

[TestClass]
public class LocalTest
{
private AndroidDriver<AndroidElement>? driver;
private string configFile = "LocalTest.json";
private Local? browserStackLocal;

[TestInitialize]
public void TestSetup()
{
Utils utils = new Utils(null, "Config/" + configFile);
AppiumOptions capabilities = utils.capabilities();

// Setup Browserstack Local
browserStackLocal = new Local();
List<KeyValuePair<string, string>> bsLocalArgs = new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key", utils.accessKey()) };
browserStackLocal.start(bsLocalArgs);

driver = new AndroidDriver<AndroidElement>(new Uri(Utils.HUB_URL), capabilities);
}

[TestMethod]
public void LocalNetworkTest()
{
if (driver == null)
throw new Exception("Could not run tests. Driver not initialised");

var _ = driver.PageSource;
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));

var testActionId = By.Id("com.example.android.basicnetworking:id/test_action");
AndroidElement testActionElement = (AndroidElement)wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementToBeClickable(testActionId));
testActionElement.Click();
Thread.Sleep(5000);

_ = driver.PageSource;

ReadOnlyCollection<AndroidElement> textviewElements = driver.FindElementsByClassName("android.widget.TextView");
string text = textviewElements.Last().Text;

Assert.IsTrue(text.Contains("The active connection is wifi"));
Assert.IsTrue(text.Contains("Up and running"));

driver.ExecuteScript("browserstack_executor: { \"action\": \"setSessionStatus\", \"arguments\": { \"status\": \"passed\", \"reason\": \"Test Passed!\"} }");
}

[TestCleanup]
public void TestTeardown()
{
if (driver != null)
driver.Quit();

if (browserStackLocal != null)
browserStackLocal.stop();
}
}

19 changes: 19 additions & 0 deletions Android/ParallelTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.MethodLevel)]

namespace Android;

[TestClass]
public class ParallelTest
{
[TestMethod]
[DataRow("samsungs10", "ParallelTest.json")]
[DataRow("pixel3", "ParallelTest.json")]
public void TestMethod1(string configName, string configFile)
{
var singleTest = new SingleTest(configName, configFile);
singleTest.TestSetup();
singleTest.SearchTest();
singleTest.TestTeardown();
}
}

63 changes: 63 additions & 0 deletions Android/SingleTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Collections.ObjectModel;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;

namespace Android;

[TestClass]
public class SingleTest
{
private AndroidDriver<AndroidElement>? driver;
private string configFile = "SingleTest.json";
private string? configName;

public SingleTest() { }

public SingleTest(string _configName, string _configFile)
{
configName = _configName;
configFile = _configFile;
}

[TestInitialize]
public void TestSetup()
{
Utils utils = new Utils(configName, "Config/" + configFile);
driver = new AndroidDriver<AndroidElement>(new Uri(Utils.HUB_URL), utils.capabilities());
}

[TestMethod]
public void SearchTest()
{
if (driver == null)
throw new Exception("Could not run tests. Driver not initialised");

var _ = driver.PageSource;
var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));

var searchElementId = MobileBy.AccessibilityId("Search Wikipedia");
AndroidElement searchElement = (AndroidElement)wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementToBeClickable(searchElementId));
searchElement.Click();
Thread.Sleep(2000);

_ = driver.PageSource;

wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
var insertTextElementId = By.Id("org.wikipedia.alpha:id/search_src_text");
AndroidElement insertTextElement = (AndroidElement)wait.Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementToBeClickable(insertTextElementId));
insertTextElement.SendKeys("BrowserStack");
Thread.Sleep(5000);

ReadOnlyCollection<AndroidElement> allProductsName = driver.FindElements(By.ClassName("android.widget.TextView"));
Assert.IsTrue(allProductsName.Count > 0);

driver.ExecuteScript("browserstack_executor: { \"action\": \"setSessionStatus\", \"arguments\": { \"status\": \"passed\", \"reason\": \"Test Passed!\"} }");
}

[TestCleanup]
public void TestTeardown()
{
if (driver != null)
driver.Quit();
}
}
4 changes: 4 additions & 0 deletions Android/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
global using Microsoft.VisualStudio.TestTools.UnitTesting;
global using OpenQA.Selenium.Appium;
global using OpenQA.Selenium.Appium.Android;
global using Common;
13 changes: 13 additions & 0 deletions Common/Common.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Appium.WebDriver" Version="4.4.5" />
</ItemGroup>
</Project>
94 changes: 94 additions & 0 deletions Common/Utils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System.Reflection;
using Newtonsoft.Json.Linq;
using OpenQA.Selenium.Appium;

namespace Common;

public class Utils
{
public const string HUB_URL = "http://hub-cloud.browserstack.com/wd/hub";
public JObject testConfig;

public Utils(string configName, string configPath)
{
string? currentDirectory = GoUpLevels(Directory.GetCurrentDirectory(), 3);
testConfig = JObject.Parse(File.ReadAllText(Path.Combine(currentDirectory, configPath)));

// ConfigName is present for parallel test configs.
if (configName is not null)
{
JObject targetConfig = (JObject)testConfig.GetValue(configName);
if (targetConfig is null)
throw new Exception("Config Name used for invoking parallel test is not valid");

testConfig = (JObject)testConfig.GetValue("commonCaps");
testConfig.Merge(targetConfig);
}

if (testConfig is null)
throw new Exception("Could not parse config from the config json file");
}

public AppiumOptions capabilities()
{
AppiumOptions capabilities = new AppiumOptions();
Dictionary<string, object> browserstackOptions = new Dictionary<string, object>();

// Parse all keys at root level and build AppiumOptions out of it.
foreach (var property in testConfig.Properties())
{
if (property.Value.Type != JTokenType.Object)
capabilities.AddAdditionalCapability(property.Name, (JValue)property.Value);
}
// Update bstack:options with user creds. value in config file takes precedence over env variable.
if (!testConfig.ContainsKey("bstack:options"))
testConfig.Add("bstack:options", new JObject());

testConfig["bstack:options"]["userName"] = username();
testConfig["bstack:options"]["accessKey"] = accessKey();

// Parse nested bstack:options and push into AppiumOptions
JObject bStackOptions = (JObject)testConfig["bstack:options"];
foreach (var property in bStackOptions.Properties())
{
if (property.Value.Type != JTokenType.Object)
browserstackOptions.Add(property.Name, (JValue)property.Value);
}

capabilities.AddAdditionalCapability("bstack:options", browserstackOptions);

return capabilities;
}

private string? username()
{
string? username = (string?)testConfig["bstack:options"]["userName"];
if (username is null)
username = Environment.GetEnvironmentVariable("BROWSERSTACK_USERNAME_OLD");

return username;
}

public string? accessKey()
{
string? accessKey = (string?)testConfig["bstack:options"]["accessKey"];
if (accessKey is null)
accessKey = Environment.GetEnvironmentVariable("BROWSERSTACK_ACCESS_KEY_OLD");

return accessKey;
}

private static string GoUpLevels(string path, int levels)
{
// Combine with ".." for each level to go up
string newPath = path;
for (int i = 0; i < levels; i++)
{
newPath = Path.Combine(newPath, "..");
}

// Get the full path after going up the specified levels
return Path.GetFullPath(newPath);
}
}

Loading

0 comments on commit 0cbdf17

Please sign in to comment.