diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 000000000..0926a8ef8
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,10 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(dotnet build:*)",
+ "Bash(powershell -Command \"Get-ChildItem -Path . -Recurse -Include *.cs | ForEach-Object { (Get-Content $_.FullName) -replace ''Assert\\.AreEqual\\(true,\\s*'', ''Assert.IsTrue('' | Set-Content $_.FullName }\")",
+ "Bash(powershell:*)",
+ "Bash(dotnet clean:*)"
+ ]
+ }
+}
diff --git a/Bellatrix.LLM/Bellatrix.LLM.csproj b/Bellatrix.LLM/Bellatrix.LLM.csproj
index ef6a46516..9a7171d19 100644
--- a/Bellatrix.LLM/Bellatrix.LLM.csproj
+++ b/Bellatrix.LLM/Bellatrix.LLM.csproj
@@ -1,23 +1,24 @@
-
- net8.0
- enable
- enable
-
+
+ net10.0
+ enable
+ enable
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/Bellatrix.LLM/SemanticKernelService.cs b/Bellatrix.LLM/SemanticKernelService.cs
index 5cd6e687b..6b420f1b3 100644
--- a/Bellatrix.LLM/SemanticKernelService.cs
+++ b/Bellatrix.LLM/SemanticKernelService.cs
@@ -54,7 +54,9 @@ private static void EnsureInitialized()
return;
var llmSettings = ConfigurationService.GetSection();
-
+ if (llmSettings == null)
+ return;
+
var genSettings = llmSettings.ModelSettings[0];
var embedSettings = llmSettings.ModelSettings[1];
diff --git a/Bellatrix.LLM/assertions/AiAssert.cs b/Bellatrix.LLM/assertions/AiAssert.cs
index 377650fd3..ddee98de7 100644
--- a/Bellatrix.LLM/assertions/AiAssert.cs
+++ b/Bellatrix.LLM/assertions/AiAssert.cs
@@ -41,7 +41,7 @@ public static void AssertByPrompt(string assertInstruction)
if (string.IsNullOrWhiteSpace(verdict) || !verdict.Contains("PASS", StringComparison.OrdinalIgnoreCase))
{
- Assert.Fail($"AI Assert failed: {assertInstruction} - {verdict}");
+ Assertions.Assert.Fail($"AI Assert failed: {assertInstruction} - {verdict}");
}
Console.WriteLine("✅ AI Assert passed: " + assertInstruction);
diff --git a/Bellatrix.LLM/plugins/SmartFailureAnalysisPlugin .cs b/Bellatrix.LLM/plugins/SmartFailureAnalysisPlugin .cs
index 51343ca54..ffa4dcd7b 100644
--- a/Bellatrix.LLM/plugins/SmartFailureAnalysisPlugin .cs
+++ b/Bellatrix.LLM/plugins/SmartFailureAnalysisPlugin .cs
@@ -15,6 +15,7 @@
// The architecture and agent logic are original contributions by Anton Angelov, forming the foundation for a PhD dissertation.
// Please cite or credit appropriately if reusing in academic or commercial work.
+using Bellatrix.LLM.Settings;
using Bellatrix.Plugins.Screenshots.Contracts;
using Bellatrix.Plugins;
using Bellatrix.Plugins.Screenshots.Plugins;
@@ -26,12 +27,15 @@ public class SmartFailureAnalysisPlugin : Plugin, IScreenshotPlugin
private readonly IScreenshotOutputProvider _screenshotOutputProvider;
private readonly IViewSnapshotProvider _viewSnapshotProvider;
private static ThreadLocal _screenshotPath = new ThreadLocal();
+ private readonly bool _isEnabled;
public SmartFailureAnalysisPlugin()
{
_screenshotOutputProvider = ServicesCollection.Current.Resolve();
_viewSnapshotProvider = ServicesCollection.Main.Resolve();
+ var largeLanguageModelsSettings = ConfigurationService.GetSection();
+ _isEnabled = largeLanguageModelsSettings?.EnableSmartFailureAnalysis ?? false;
}
public static void Add()
@@ -60,6 +64,7 @@ public void ScreenshotGenerated(object sender, ScreenshotPluginEventArgs args)
protected override void PreTestCleanup(object sender, PluginEventArgs e)
{
+ if (!_isEnabled) return;
if (e.TestOutcome == TestOutcome.Passed)
{
var log = Logger.GetLogs();
@@ -87,7 +92,7 @@ private void RunFailureAnalysisAsync(PluginEventArgs e)
snapshot,
_screenshotPath.Value ?? string.Empty);
-
+
}
catch (Exception ex)
{
@@ -112,4 +117,4 @@ private void RunFailureAnalysisAsync(PluginEventArgs e)
//{extended}
//""");
}
-}
+}
\ No newline at end of file
diff --git a/shared/SharedAssemblyInfo.targets b/shared/SharedAssemblyInfo.targets
index e1aefd6d6..2fa77f9a4 100644
--- a/shared/SharedAssemblyInfo.targets
+++ b/shared/SharedAssemblyInfo.targets
@@ -1,34 +1,24 @@
- net8.0
- 11.0
+ net10.0
+ 14.0
DEBUG;STAGE;QA;LOCAL
- 3.9.0.0
+ 3.9.5.0
Automate The Planet Ltd.
Automate The Planet Ltd.
Bellatrix Test Framework
- 3.9.0.0
- 3.9.0.0
+ 3.9.5.0
+ 3.9.5.0
en
Automate The Planet Ltd. 2024
- - Added DevExtreme Components v1.2.0
- - Fixed parallel execution
- - Added Excel Service
- - Fixed formatting
- - Added qTestId plugin
- - Added Advanced CkEditor component
- - Added DevExtreme Components v.1.0
- - .NET 8 Support
- - Selenium Manager Support
- - Upgrade All NuGet Packages
- - Chrome Headless uses the new headless mode now
- - Fixed grid execution driver additional options to Selenium 4
- - Added SetModelColumns method to grid extensions
- - Upgraded Azure KeyVault Identity
- - Upgraded WebDriver Manager Version
+ - added many new improvements to desktop module
+ - added support for .NET 10
+ - added support to Appium 3.x
+ - improved test execution performance
+ - fix some LLM related issues
- upgrade all 3rd-party NuGet packages
- fix all known bugs
@@ -45,7 +35,7 @@
true
true
- NU1701;NU1702;NU1705;NU1608;
+ NU1701;NU1702;NU1705;NU1608;NU1903;NU1902;NU1904;MSTEST0001
true
@@ -55,6 +45,6 @@
false
false
true
- NU1701;NU1702;NU1705;NU1608;
+ NU1701;NU1702;NU1705;NU1608;MSTEST0001
-
+
\ No newline at end of file
diff --git a/src/Bellatrix.AWS/Bellatrix.AWS.csproj b/src/Bellatrix.AWS/Bellatrix.AWS.csproj
index 123ec851e..1a06ae1c1 100644
--- a/src/Bellatrix.AWS/Bellatrix.AWS.csproj
+++ b/src/Bellatrix.AWS/Bellatrix.AWS.csproj
@@ -1,19 +1,19 @@
-
-
-
-
+
+
+
+
-
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Bellatrix.Allure/Bellatrix.Results.Allure.csproj b/src/Bellatrix.Allure/Bellatrix.Results.Allure.csproj
index 37cd49b63..93a0f7986 100644
--- a/src/Bellatrix.Allure/Bellatrix.Results.Allure.csproj
+++ b/src/Bellatrix.Allure/Bellatrix.Results.Allure.csproj
@@ -3,7 +3,7 @@
-
+
diff --git a/src/Bellatrix.Api/Bellatrix.API.csproj b/src/Bellatrix.Api/Bellatrix.API.csproj
index 68cbcb806..dbf684743 100644
--- a/src/Bellatrix.Api/Bellatrix.API.csproj
+++ b/src/Bellatrix.Api/Bellatrix.API.csproj
@@ -1,20 +1,20 @@
- NU1701;NU1702;NU1705;NU1608;NU1902;
+ NU1701;NU1702;NU1705;NU1608;NU1902;MSTEST0001
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/Bellatrix.BugReporting/Bellatrix.BugReporting.csproj b/src/Bellatrix.BugReporting/Bellatrix.BugReporting.csproj
index de2276813..23604a3f7 100644
--- a/src/Bellatrix.BugReporting/Bellatrix.BugReporting.csproj
+++ b/src/Bellatrix.BugReporting/Bellatrix.BugReporting.csproj
@@ -11,10 +11,10 @@
-
-
-
-
+
+
+
+
diff --git a/src/Bellatrix.CognitiveServices/Bellatrix.CognitiveServices.csproj b/src/Bellatrix.CognitiveServices/Bellatrix.CognitiveServices.csproj
index d498f9ae6..72153d76a 100644
--- a/src/Bellatrix.CognitiveServices/Bellatrix.CognitiveServices.csproj
+++ b/src/Bellatrix.CognitiveServices/Bellatrix.CognitiveServices.csproj
@@ -3,7 +3,7 @@
-
+
diff --git a/src/Bellatrix.CognitiveServices/services/AssertedTableFormCell.cs b/src/Bellatrix.CognitiveServices/services/AssertedTableFormCell.cs
index 0ea43de84..25fb5a442 100644
--- a/src/Bellatrix.CognitiveServices/services/AssertedTableFormCell.cs
+++ b/src/Bellatrix.CognitiveServices/services/AssertedTableFormCell.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
diff --git a/src/Bellatrix.Core/Bellatrix.Core.csproj b/src/Bellatrix.Core/Bellatrix.Core.csproj
index 2e453d156..5aa56203b 100644
--- a/src/Bellatrix.Core/Bellatrix.Core.csproj
+++ b/src/Bellatrix.Core/Bellatrix.Core.csproj
@@ -11,25 +11,25 @@
-
+
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
diff --git a/src/Bellatrix.Core/assertions/MSTestAssert.cs b/src/Bellatrix.Core/assertions/MSTestAssert.cs
index b93b6269f..1ecfec026 100644
--- a/src/Bellatrix.Core/assertions/MSTestAssert.cs
+++ b/src/Bellatrix.Core/assertions/MSTestAssert.cs
@@ -71,7 +71,7 @@ public void AreDateTimesEqual(DateTime? expectedDate, DateTime? actualDate, int
public void Fail(string message) => MU.Assert.Fail(message);
- public void Fail(string message, params object[] parameters) => MU.Assert.Fail(message, parameters);
+ public void Fail(string message, params object[] parameters) => MU.Assert.Fail(string.Format(message, parameters));
public void IsFalse(bool condition) => MU.Assert.IsFalse(condition);
@@ -93,7 +93,7 @@ public void AreDateTimesEqual(DateTime? expectedDate, DateTime? actualDate, int
public void IsTrue(bool condition, string message) => MU.Assert.IsTrue(condition, message);
- public void IsTrue(bool condition, string message, params object[] parameters) => MU.Assert.IsTrue(condition, message, parameters);
+ public void IsTrue(bool condition, string message, params object[] parameters) => MU.Assert.IsTrue(condition, string.Format(message, parameters));
public void Multiple(params Action[] assertions) => Assert.Multiple(assertions);
}
\ No newline at end of file
diff --git a/src/Bellatrix.Core/utilities/WindowsProcessExtensions.cs b/src/Bellatrix.Core/utilities/WindowsProcessExtensions.cs
index 530855b1f..4f6596364 100644
--- a/src/Bellatrix.Core/utilities/WindowsProcessExtensions.cs
+++ b/src/Bellatrix.Core/utilities/WindowsProcessExtensions.cs
@@ -32,7 +32,7 @@ public static IEnumerable GetChildProcesses(this Process process)
return children;
}
-
+
public static Process GetParentProcess(this Process process)
{
int parentPid = 0;
diff --git a/src/Bellatrix.Desktop/Bellatrix.Desktop.csproj b/src/Bellatrix.Desktop/Bellatrix.Desktop.csproj
index e06f365f1..fc9cd07e3 100644
--- a/src/Bellatrix.Desktop/Bellatrix.Desktop.csproj
+++ b/src/Bellatrix.Desktop/Bellatrix.Desktop.csproj
@@ -2,20 +2,15 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
diff --git a/src/Bellatrix.Desktop/DesktopPluginsConfiguration.cs b/src/Bellatrix.Desktop/DesktopPluginsConfiguration.cs
index 33e5ff03b..5800ed76b 100644
--- a/src/Bellatrix.Desktop/DesktopPluginsConfiguration.cs
+++ b/src/Bellatrix.Desktop/DesktopPluginsConfiguration.cs
@@ -150,7 +150,8 @@ public static void ConfigureLLM()
{
if (ConfigurationService.GetSection() == null)
{
- throw new ArgumentException("Could not load LargeLanguageModelsSettings section from testFrameworkSettings.json");
+ Logger.LogError("Could not load LargeLanguageModelsSettings section from testFrameworkSettings.json");
+ return;
}
try
@@ -174,4 +175,4 @@ public static void ConfigureLLM()
Logger.LogError(ex.ToString());
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/components/Checkbox.cs b/src/Bellatrix.Desktop/components/CheckBox.cs
similarity index 74%
rename from src/Bellatrix.Desktop/components/Checkbox.cs
rename to src/Bellatrix.Desktop/components/CheckBox.cs
index 6b4eabd34..cfe3704be 100644
--- a/src/Bellatrix.Desktop/components/Checkbox.cs
+++ b/src/Bellatrix.Desktop/components/CheckBox.cs
@@ -31,7 +31,16 @@ public virtual void Check(bool isChecked = true)
{
if (isChecked && !WrappedElement.Selected || !isChecked && WrappedElement.Selected)
{
- Click(Checking, Checked);
+ Checking?.Invoke(this, new ComponentActionEventArgs(this));
+ try
+ {
+ WrappedDriver.ExecuteScript("windows: toggle", WrappedElement);
+ }
+ catch
+ {
+ Click(null, null);
+ }
+ Checked?.Invoke(this, new ComponentActionEventArgs(this));
}
}
@@ -39,7 +48,16 @@ public virtual void Uncheck()
{
if (WrappedElement.Selected)
{
- Click(Unchecking, Unchecked);
+ Unchecking?.Invoke(this, new ComponentActionEventArgs(this));
+ try
+ {
+ WrappedDriver.ExecuteScript("windows: toggle", WrappedElement);
+ }
+ catch
+ {
+ Click(null, null);
+ }
+ Unchecked?.Invoke(this, new ComponentActionEventArgs(this));
}
}
diff --git a/src/Bellatrix.Desktop/components/ComboBox.cs b/src/Bellatrix.Desktop/components/ComboBox.cs
index dc47d359c..f28a6fca9 100644
--- a/src/Bellatrix.Desktop/components/ComboBox.cs
+++ b/src/Bellatrix.Desktop/components/ComboBox.cs
@@ -13,8 +13,11 @@
// https://bellatrix.solutions/
using System;
using System.Diagnostics;
+using System.Linq;
using Bellatrix.Desktop.Contracts;
using Bellatrix.Desktop.Events;
+using Bellatrix.Desktop.Services;
+using OpenQA.Selenium.Appium;
namespace Bellatrix.Desktop;
@@ -34,16 +37,65 @@ public virtual void SelectByText(string value)
{
Selecting?.Invoke(this, new ComponentActionEventArgs(this, value));
- if (WrappedElement.Text != value)
+ try
{
- WrappedElement.SendKeys(value);
+ var itemToSelect = this.CreateAllByTag("ListItem")
+ .FirstOrDefault(x => x.CreateByTag
// Anton Angelov
// https://bellatrix.solutions/
+
using System;
+using System.Collections.Generic;
using Bellatrix.Desktop.Contracts;
using Bellatrix.Desktop.Events;
using Bellatrix.Layout;
-using OpenQA.Selenium;
-using OpenQA.Selenium.Remote;
+using OpenQA.Selenium.Interactions;
namespace Bellatrix.Desktop;
@@ -25,8 +26,39 @@ public partial class Component : IComponentVisible, IComponent, ILayoutComponent
internal virtual void Click(EventHandler clicking, EventHandler clicked)
{
clicking?.Invoke(this, new ComponentActionEventArgs(this));
+ var clickSuccess = false;
+
+ try
+ {
+ WrappedDriver.ExecuteScript("windows: invoke", WrappedElement);
+ clickSuccess = true;
+ }
+ catch
+ {
+ // ignore
+ }
+
+ if (!clickSuccess)
+ {
+ try
+ {
+ this.ToBeVisible().WrappedElement.Click();
+ clickSuccess = true;
+ }
+ catch
+ {
+ Logger.LogWarning($"Could not click component {ComponentName}." +
+ $"Component location: X={Location.X}, Y={Location.Y}" +
+ $"Component size: Width={Size.Width}, Height={Size.Height}");
+ }
+ }
- WrappedElement.Click();
+ if (!clickSuccess)
+ {
+ new Actions(WrappedDriver, TimeSpan.Zero)
+ .MoveToElement(WrappedElement)
+ .Click().Perform();
+ }
clicked?.Invoke(this, new ComponentActionEventArgs(this));
}
@@ -35,7 +67,19 @@ internal virtual void Hover(EventHandler hovering, Eve
{
hovering?.Invoke(this, new ComponentActionEventArgs(this));
- WrappedDriver.Mouse.MouseMove(WrappedElement.Coordinates);
+ try
+ {
+ WrappedDriver.ExecuteScript("windows: hover", new Dictionary
+ {
+ { "startElementId", WrappedElement.Id },
+ { "endElementId", WrappedElement.Id },
+ { "durationMs", 0 }
+ });
+ }
+ catch
+ {
+ new Actions(WrappedDriver).MoveToElement(WrappedElement).Perform();
+ }
hovered?.Invoke(this, new ComponentActionEventArgs(this));
}
@@ -53,8 +97,19 @@ internal virtual bool GetIsDisabled()
internal virtual void SetText(EventHandler settingValue, EventHandler valueSet, string value)
{
settingValue?.Invoke(this, new ComponentActionEventArgs(this, value));
- WrappedElement.Clear();
- WrappedElement.SendKeys(value);
+ var element = WrappedElement;
+
+ try
+ {
+ WrappedDriver.ExecuteScript("windows: setValue", element, value);
+ valueSet?.Invoke(this, new ComponentActionEventArgs(this, value));
+ }
+ catch
+ {
+ WrappedElement.Clear();
+ WrappedElement.SendKeys(value);
+ }
+
valueSet?.Invoke(this, new ComponentActionEventArgs(this, value));
}
}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/components/Core/Component.cs b/src/Bellatrix.Desktop/components/Core/Component.cs
index 420d604e9..c29be5526 100644
--- a/src/Bellatrix.Desktop/components/Core/Component.cs
+++ b/src/Bellatrix.Desktop/components/Core/Component.cs
@@ -28,7 +28,9 @@
using Bellatrix.LLM;
using Bellatrix.Plugins.Screenshots;
using OpenQA.Selenium;
+using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
+using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Remote;
namespace Bellatrix.Desktop;
@@ -38,14 +40,14 @@ public partial class Component
{
private readonly ComponentWaitService _elementWait;
private readonly List _untils;
- private WindowsElement _wrappedElement;
+ private AppiumElement _wrappedElement;
private IViewSnapshotProvider _viewSnapshotProvider;
private LargeLanguageModelsSettings _llmSettings;
public Component()
{
_elementWait = new ComponentWaitService();
- WrappedDriver = ServicesCollection.Current.Resolve>();
+ WrappedDriver = ServicesCollection.Current.Resolve();
_viewSnapshotProvider = ServicesCollection.Current.Resolve();
_llmSettings = ConfigurationService.GetSection();
_untils = new List();
@@ -59,22 +61,22 @@ public Component()
public static event EventHandler CreatedComponents;
public static event EventHandler ReturningWrappedElement;
- public WindowsDriver WrappedDriver { get; }
+ public WindowsDriver WrappedDriver { get; }
- public WindowsElement WrappedElement
+ public AppiumElement WrappedElement
{
get
{
- ReturningWrappedElement?.Invoke(this, new NativeElementActionEventArgs(GetAndWaitWebDriverElement()));
- var element = GetWebDriverElement();
+ var element = GetAndWaitWebDriverElement();
+ ReturningWrappedElement?.Invoke(this, new NativeElementActionEventArgs(element));
return element;
}
internal set => _wrappedElement = value;
}
- public WindowsElement ParentWrappedElement { get; set; }
+ public AppiumElement ParentWrappedElement { get; set; }
- public WindowsElement FoundWrappedElement { get; set; }
+ public AppiumElement FoundWrappedElement { get; set; }
public int ElementIndex { get; set; }
@@ -186,11 +188,14 @@ public virtual bool IsVisible
public virtual void ScrollToVisible()
{
ScrollingToVisible?.Invoke(this, new ComponentActionEventArgs(this));
-
- var touchActions = new RemoteTouchScreen(WrappedDriver);
- System.Threading.Thread.Sleep(2000);
- touchActions.Scroll(WrappedElement.Coordinates, 0, 0);
- this.ToBeVisible().ToExists().WaitToBe();
+ try
+ {
+ WrappedDriver.ExecuteScript("windows: scrollToVisible", WrappedElement);
+ }
+ catch
+ {
+ // ignore
+ }
ScrolledToVisible?.Invoke(this, new ComponentActionEventArgs(this));
}
@@ -218,7 +223,7 @@ public override string ToString()
return sb.ToString();
}
- protected WindowsElement GetAndWaitWebDriverElement()
+ protected AppiumElement GetAndWaitWebDriverElement()
{
if (_wrappedElement == null)
{
@@ -234,20 +239,21 @@ protected WindowsElement GetAndWaitWebDriverElement()
if (until != null)
{
_elementWait.Wait(this, until);
- if (until.GetType() == typeof(WaitNotExistStrategy))
- {
- return _wrappedElement;
- }
+ }
+
+ if (until is WaitNotExistStrategy)
+ {
+ return _wrappedElement;
}
}
_wrappedElement = GetWebDriverElement();
// ✅ Save if healing is enabled
- if (_llmSettings.EnableSelfHealing)
+ if (_llmSettings != null && _llmSettings.EnableSelfHealing)
{
var snapshot = _viewSnapshotProvider.GetCurrentViewSnapshot();
- LocatorSelfHealingService.SaveWorkingLocator(By.ToString(), snapshot, WrappedDriver.Title);
+ LocatorSelfHealingService.SaveWorkingLocator(By.ToString(), snapshot, WrappedDriver.CurrentWindowHandle);
}
_untils.Clear();
@@ -255,7 +261,7 @@ protected WindowsElement GetAndWaitWebDriverElement()
}
catch (Exception ex)
{
- if (!_llmSettings.EnableSelfHealing)
+ if (_llmSettings == null || !_llmSettings.EnableSelfHealing)
{
throw new TimeoutException($"❌ Element not found: {By?.Value}", ex);
}
@@ -263,7 +269,7 @@ protected WindowsElement GetAndWaitWebDriverElement()
Logger.LogWarning($"⚠️ Element not found with locator: {By}. Trying AI-based healing...");
var snapshot = _viewSnapshotProvider.GetCurrentViewSnapshot();
- var healedXpath = LocatorSelfHealingService.TryHeal(By.ToString(), snapshot, WrappedDriver.Title);
+ var healedXpath = LocatorSelfHealingService.TryHeal(By.ToString(), snapshot, WrappedDriver.CurrentWindowHandle);
if (!string.IsNullOrEmpty(healedXpath))
{
@@ -285,8 +291,9 @@ protected WindowsElement GetAndWaitWebDriverElement()
return _wrappedElement;
}
- private WindowsElement GetWebDriverElement()
+ private AppiumElement GetWebDriverElement()
{
+ var result = _wrappedElement;
if (FoundWrappedElement != null)
{
return FoundWrappedElement;
@@ -299,4 +306,4 @@ private WindowsElement GetWebDriverElement()
return By.FindElement(WrappedDriver);
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/components/Core/ComponentsList.cs b/src/Bellatrix.Desktop/components/Core/ComponentsList.cs
index e659b9212..16938e89d 100644
--- a/src/Bellatrix.Desktop/components/Core/ComponentsList.cs
+++ b/src/Bellatrix.Desktop/components/Core/ComponentsList.cs
@@ -19,6 +19,7 @@
using Bellatrix.Desktop.Locators;
using Bellatrix.Desktop.Services;
using Bellatrix.Desktop.Untils;
+using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Controls.Core;
@@ -27,14 +28,14 @@ public class ComponentsList : IEnumerable
where TComponent : Component
{
private readonly FindStrategy _by;
- private readonly WindowsElement _parenTComponent;
+ private readonly AppiumElement _parenTComponent;
private readonly List _foundElements;
private readonly bool _shouldCacheFoundElements;
private List _cachedElements;
public ComponentsList(
FindStrategy by,
- WindowsElement parenTComponent,
+ AppiumElement parenTComponent,
bool shouldCacheFoundElements)
: this(by, parenTComponent)
{
@@ -43,27 +44,27 @@ public ComponentsList(
public ComponentsList(
FindStrategy by,
- WindowsElement parenTComponent)
+ AppiumElement parenTComponent)
{
_by = by;
_parenTComponent = parenTComponent;
_foundElements = new List();
- WrappedDriver = ServicesCollection.Current.Resolve>();
+ WrappedDriver = ServicesCollection.Current.Resolve();
}
public ComponentsList()
{
_foundElements = new List();
- WrappedDriver = ServicesCollection.Current.Resolve>();
+ WrappedDriver = ServicesCollection.Current.Resolve();
}
public ComponentsList(IEnumerable nativeElementList)
{
_foundElements = new List(nativeElementList);
- WrappedDriver = ServicesCollection.Current.Resolve>();
+ WrappedDriver = ServicesCollection.Current.Resolve();
}
- public WindowsDriver WrappedDriver { get; }
+ public WindowsDriver WrappedDriver { get; }
public TComponent this[int i] => GetAndWaitWebDriverElements().ElementAt(i);
@@ -79,7 +80,7 @@ public int Count()
}
var nativeElements = WaitWebDriverElements();
- return nativeElements.Count();
+ return nativeElements.Count;
}
public void ForEach(Action action)
@@ -130,7 +131,7 @@ IEnumerable GetAndWaitNativeElements()
foreach (var nativeElement in _by?.FindAllElements(_parenTComponent))
{
var element =
- elementRepository.CreateComponentThatIsFound(_by, (WindowsElement)nativeElement);
+ elementRepository.CreateComponentThatIsFound(_by, nativeElement);
yield return element;
}
}
diff --git a/src/Bellatrix.Desktop/components/EventHandlers/ListItemEventHandlers.cs b/src/Bellatrix.Desktop/components/EventHandlers/ListItemEventHandlers.cs
new file mode 100644
index 000000000..a7cb1996b
--- /dev/null
+++ b/src/Bellatrix.Desktop/components/EventHandlers/ListItemEventHandlers.cs
@@ -0,0 +1,33 @@
+//
+// Copyright 2025 Automate The Planet Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Anton Angelov
+// https://bellatrix.solutions/
+using Bellatrix.Desktop.Events;
+
+namespace Bellatrix.Desktop.EventHandlers;
+
+public class ListItemEventHandlers : ComponentEventHandlers
+{
+ public override void SubscribeToAll()
+ {
+ base.SubscribeToAll();
+ ListItem.Hovering += HoveringEventHandler;
+ ListItem.Hovered += HoveredEventHandler;
+ }
+
+ public override void UnsubscribeToAll()
+ {
+ base.UnsubscribeToAll();
+ ListItem.Hovering -= HoveringEventHandler;
+ ListItem.Hovered -= HoveredEventHandler;
+ }
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/components/ListItem.cs b/src/Bellatrix.Desktop/components/ListItem.cs
new file mode 100644
index 000000000..75277e2f2
--- /dev/null
+++ b/src/Bellatrix.Desktop/components/ListItem.cs
@@ -0,0 +1,49 @@
+//
+// Copyright 2025 Automate The Planet Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Anton Angelov
+// https://bellatrix.solutions/
+using System;
+using System.Diagnostics;
+using Bellatrix.Desktop.Events;
+
+namespace Bellatrix.Desktop;
+
+public class ListItem : Component
+{
+ public static event EventHandler Hovering;
+ public static event EventHandler Hovered;
+
+ public virtual string InnerText => this.CreateByTag("Text").InnerText;
+
+ public virtual void Hover()
+ {
+ Hover(Hovering, Hovered);
+ }
+
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ public virtual bool IsDisabled => GetIsDisabled();
+
+ public virtual void Select()
+ {
+ WrappedDriver.ExecuteScript("windows: select", WrappedElement);
+ }
+
+ public virtual void AddToSelection()
+ {
+ WrappedDriver.ExecuteScript("windows: addToSelection", WrappedElement);
+ }
+
+ public virtual void RemoveFromSelection()
+ {
+ WrappedDriver.ExecuteScript("windows: removeFromSelection", WrappedElement);
+ }
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/components/Menu.cs b/src/Bellatrix.Desktop/components/Menu.cs
index b2d067605..b3084e9dc 100644
--- a/src/Bellatrix.Desktop/components/Menu.cs
+++ b/src/Bellatrix.Desktop/components/Menu.cs
@@ -12,6 +12,7 @@
// Anton Angelov
// https://bellatrix.solutions/
using System;
+using System.Linq;
using Bellatrix.Desktop.Events;
namespace Bellatrix.Desktop;
@@ -25,4 +26,19 @@ public virtual void Hover()
{
Hover(Hovering, Hovered);
}
+
+ public void SelectOption(string option)
+ {
+ this.CreateAllByClass
// Anton Angelov
// https://bellatrix.solutions/
-using OpenQA.Selenium.Appium.Windows;
+
+using OpenQA.Selenium.Appium;
namespace Bellatrix.Desktop.Contracts;
@@ -21,5 +22,5 @@ public interface IComponent
string PageName { get; }
- WindowsElement WrappedElement { get; }
+ AppiumElement WrappedElement { get; }
}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/events/NativeElementActionEventArgs.cs b/src/Bellatrix.Desktop/events/NativeElementActionEventArgs.cs
index f171feb8e..8b5f6956e 100644
--- a/src/Bellatrix.Desktop/events/NativeElementActionEventArgs.cs
+++ b/src/Bellatrix.Desktop/events/NativeElementActionEventArgs.cs
@@ -11,13 +11,15 @@
//
// Anton Angelov
// https://bellatrix.solutions/
+
+using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Events;
public class NativeElementActionEventArgs
{
- public NativeElementActionEventArgs(WindowsElement element) => Element = element;
+ public NativeElementActionEventArgs(AppiumElement element) => Element = element;
- public WindowsElement Element { get; }
+ public AppiumElement Element { get; }
}
diff --git a/src/Bellatrix.Desktop/findstrategies/ComponentRepositoryExtensions.cs b/src/Bellatrix.Desktop/findstrategies/ComponentRepositoryExtensions.cs
index 3e6d8d16e..bfffe5013 100644
--- a/src/Bellatrix.Desktop/findstrategies/ComponentRepositoryExtensions.cs
+++ b/src/Bellatrix.Desktop/findstrategies/ComponentRepositoryExtensions.cs
@@ -65,4 +65,4 @@ public static ComponentsList CreateAllByAutomationId(thi
public static ComponentsList CreateAllByXPath(this ComponentCreateService repo, string xpath)
where TComponent : Component => new ComponentsList(new FindXPathStrategy(xpath), null);
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/Find.cs b/src/Bellatrix.Desktop/findstrategies/Find.cs
index c5ec6bf02..69a820a41 100644
--- a/src/Bellatrix.Desktop/findstrategies/Find.cs
+++ b/src/Bellatrix.Desktop/findstrategies/Find.cs
@@ -18,4 +18,4 @@ public sealed class Find
static Find() => By = new FindStrategyFactory();
public static FindStrategyFactory By { get; }
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindAccessibilityIdStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindAccessibilityIdStrategy.cs
index d8dee8696..a3f91aace 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindAccessibilityIdStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindAccessibilityIdStrategy.cs
@@ -12,8 +12,9 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using System.Linq;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -24,28 +25,18 @@ public FindAccessibilityIdStrategy(string name)
{
}
- public override WindowsElement FindElement(WindowsDriver searchContext)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- return searchContext.FindElementByAccessibilityId(Value);
+ return searchContext.FindElement(MobileBy.AccessibilityId(Value)) as AppiumElement;
}
- public override IEnumerable FindAllElements(WindowsDriver searchContext)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- return searchContext.FindElementsByAccessibilityId(Value);
- }
-
- public override AppiumWebElement FindElement(WindowsElement element)
- {
- return element.FindElementByAccessibilityId(Value);
- }
-
- public override IEnumerable FindAllElements(WindowsElement element)
- {
- return element.FindElementsByAccessibilityId(Value);
+ return searchContext.FindElements(MobileBy.AccessibilityId(Value)).Select(el => el as AppiumElement);
}
public override string ToString()
{
return $"AccessibilityId = {Value}";
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindAutomationIdStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindAutomationIdStrategy.cs
index 70021595a..bac39c46c 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindAutomationIdStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindAutomationIdStrategy.cs
@@ -12,8 +12,9 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using System.Linq;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -24,28 +25,18 @@ public FindAutomationIdStrategy(string name)
{
}
- public override WindowsElement FindElement(WindowsDriver searchContext)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- return searchContext.FindElementByAccessibilityId(Value);
+ return searchContext.FindElement(MobileBy.AccessibilityId(Value)) as AppiumElement;
}
- public override IEnumerable FindAllElements(WindowsDriver searchContext)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- return searchContext.FindElementsByAccessibilityId(Value);
- }
-
- public override AppiumWebElement FindElement(WindowsElement element)
- {
- return element.FindElementByAccessibilityId(Value);
- }
-
- public override IEnumerable FindAllElements(WindowsElement element)
- {
- return element.FindElementsByAccessibilityId(Value);
+ return searchContext.FindElements(MobileBy.AccessibilityId(Value)).Select(el => el as AppiumElement);
}
public override string ToString()
{
return $"AutomationId = {Value}";
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindClassNameStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindClassNameStrategy.cs
index 76b55bbbe..86280c1d1 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindClassNameStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindClassNameStrategy.cs
@@ -12,8 +12,9 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using System.Linq;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -24,28 +25,18 @@ public FindClassNameStrategy(string name)
{
}
- public override WindowsElement FindElement(WindowsDriver searchContext)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- return searchContext.FindElementByClassName(Value);
+ return searchContext.FindElement(MobileBy.ClassName(Value)) as AppiumElement;
}
- public override IEnumerable FindAllElements(WindowsDriver searchContext)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- return searchContext.FindElementsByClassName(Value);
- }
-
- public override AppiumWebElement FindElement(WindowsElement element)
- {
- return element.FindElementByClassName(Value);
- }
-
- public override IEnumerable FindAllElements(WindowsElement element)
- {
- return element.FindElementsByClassName(Value);
+ return searchContext.FindElements(MobileBy.ClassName(Value)).Select(el => el as AppiumElement);
}
public override string ToString()
{
return $"ClassName = {Value}";
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindIdEndingWithStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindIdEndingWithStrategy.cs
index 714abce3e..89f94dd11 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindIdEndingWithStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindIdEndingWithStrategy.cs
@@ -12,8 +12,9 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using System.Linq;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -26,28 +27,18 @@ public FindIdEndingWithStrategy(string value)
{
}
- public override WindowsElement FindElement(WindowsDriver searchContext)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- return searchContext.FindElementByXPath(string.Format(XpathEndingWithExpression, Value));
+ return searchContext.FindElement(By.XPath(string.Format(XpathEndingWithExpression, Value))) as AppiumElement;
}
- public override IEnumerable FindAllElements(WindowsDriver searchContext)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- return searchContext.FindElementsByXPath(string.Format(XpathEndingWithExpression, Value));
- }
-
- public override AppiumWebElement FindElement(WindowsElement element)
- {
- return element.FindElementByXPath(string.Format(XpathEndingWithExpression, Value));
- }
-
- public override IEnumerable FindAllElements(WindowsElement element)
- {
- return element.FindElementsByXPath(string.Format(XpathEndingWithExpression, Value));
+ return searchContext.FindElements(By.XPath(string.Format(XpathEndingWithExpression, Value))).Select(el => el as AppiumElement);
}
public override string ToString()
{
return $"By ID ending with = {Value}";
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindIdStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindIdStrategy.cs
index 2f2475156..e903899c8 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindIdStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindIdStrategy.cs
@@ -12,8 +12,9 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using System.Linq;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -24,28 +25,18 @@ public FindIdStrategy(string name)
{
}
- public override WindowsElement FindElement(WindowsDriver searchContext)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- return searchContext.FindElementById(Value);
+ return searchContext.FindElement(By.Id(Value)) as AppiumElement;
}
- public override IEnumerable FindAllElements(WindowsDriver searchContext)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- return searchContext.FindElementsById(Value);
- }
-
- public override AppiumWebElement FindElement(WindowsElement element)
- {
- return element.FindElementById(Value);
- }
-
- public override IEnumerable FindAllElements(WindowsElement element)
- {
- return element.FindElementsById(Value);
+ return searchContext.FindElements(By.Id(Value)).Select(el => el as AppiumElement);
}
public override string ToString()
{
return $"Id = {Value}";
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindNameStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindNameStrategy.cs
index b2739491d..cf19ea79a 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindNameStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindNameStrategy.cs
@@ -12,8 +12,9 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using System.Linq;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -24,28 +25,18 @@ public FindNameStrategy(string name)
{
}
- public override WindowsElement FindElement(WindowsDriver searchContext)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- return searchContext.FindElementByName(Value);
+ return searchContext.FindElement(MobileBy.Name(Value)) as AppiumElement;
}
- public override IEnumerable FindAllElements(WindowsDriver searchContext)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- return searchContext.FindElementsByName(Value);
- }
-
- public override AppiumWebElement FindElement(WindowsElement element)
- {
- return element.FindElementByName(Value);
- }
-
- public override IEnumerable FindAllElements(WindowsElement element)
- {
- return element.FindElementsByName(Value);
+ return searchContext.FindElements(MobileBy.Name(Value)).Select(el => el as AppiumElement);
}
public override string ToString()
{
return $"Name = {Value}";
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindStrategy.cs
index 096b148a4..dbdb46363 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindStrategy.cs
@@ -12,8 +12,8 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -26,11 +26,7 @@ public FindStrategy(string name)
public string Value { get; }
- public abstract WindowsElement FindElement(WindowsDriver driver);
+ public abstract AppiumElement FindElement(ISearchContext driver);
- public abstract IEnumerable FindAllElements(WindowsDriver driver);
-
- public abstract AppiumWebElement FindElement(WindowsElement element);
-
- public abstract IEnumerable FindAllElements(WindowsElement element);
+ public abstract IEnumerable FindAllElements(ISearchContext driver);
}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindStrategyCreateExtensions.cs b/src/Bellatrix.Desktop/findstrategies/FindStrategyCreateExtensions.cs
index 1e96faf09..161af0609 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindStrategyCreateExtensions.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindStrategyCreateExtensions.cs
@@ -33,4 +33,4 @@ public static ComponentsList CreateAll(this TBy by)
var elementRepository = ServicesCollection.Current.Resolve();
return elementRepository.CreateAll(by);
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindStrategyFactory.cs b/src/Bellatrix.Desktop/findstrategies/FindStrategyFactory.cs
index cba539b96..ddae4e011 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindStrategyFactory.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindStrategyFactory.cs
@@ -30,4 +30,4 @@ public class FindStrategyFactory
public FindXPathStrategy XPath(string name) => new FindXPathStrategy(name);
public FindAutomationIdStrategy AutomationId(string name) => new FindAutomationIdStrategy(name);
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindTagNameStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindTagNameStrategy.cs
index deacb23b6..f5aded8ca 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindTagNameStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindTagNameStrategy.cs
@@ -12,8 +12,9 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using System.Linq;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -24,28 +25,18 @@ public FindTagNameStrategy(string name)
{
}
- public override WindowsElement FindElement(WindowsDriver searchContext)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- return searchContext.FindElementByTagName(Value);
+ return searchContext.FindElement(By.TagName(Value)) as AppiumElement;
}
- public override IEnumerable FindAllElements(WindowsDriver searchContext)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- return searchContext.FindElementsByTagName(Value);
- }
-
- public override AppiumWebElement FindElement(WindowsElement element)
- {
- return element.FindElementByTagName(Value);
- }
-
- public override IEnumerable FindAllElements(WindowsElement element)
- {
- return element.FindElementsByTagName(Value);
+ return searchContext.FindElements(By.TagName(Value)).Select(el => el as AppiumElement);
}
public override string ToString()
{
return $"TagName = {Value}";
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/findstrategies/FindXPathStrategy.cs b/src/Bellatrix.Desktop/findstrategies/FindXPathStrategy.cs
index 6b052dbce..e5cc6c9dc 100644
--- a/src/Bellatrix.Desktop/findstrategies/FindXPathStrategy.cs
+++ b/src/Bellatrix.Desktop/findstrategies/FindXPathStrategy.cs
@@ -12,8 +12,9 @@
// Anton Angelov
// https://bellatrix.solutions/
using System.Collections.Generic;
+using System.Linq;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Locators;
@@ -24,28 +25,18 @@ public FindXPathStrategy(string name)
{
}
- public override WindowsElement FindElement(WindowsDriver searchContext)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- return searchContext.FindElementByXPath(Value);
+ return searchContext.FindElement(By.XPath(Value)) as AppiumElement;
}
- public override IEnumerable FindAllElements(WindowsDriver searchContext)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- return searchContext.FindElementsByXPath(Value);
- }
-
- public override AppiumWebElement FindElement(WindowsElement element)
- {
- return element.FindElementByXPath(Value);
- }
-
- public override IEnumerable FindAllElements(WindowsElement element)
- {
- return element.FindElementsByXPath(Value);
+ return searchContext.FindElements(By.XPath(Value)).Select(el => el as AppiumElement);
}
public override string ToString()
{
return $"ByXPath = {Value}";
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/infrastructure/App.cs b/src/Bellatrix.Desktop/infrastructure/App.cs
index 89f7f8a03..b1d59f815 100644
--- a/src/Bellatrix.Desktop/infrastructure/App.cs
+++ b/src/Bellatrix.Desktop/infrastructure/App.cs
@@ -29,20 +29,19 @@
using System.IO;
using System.Linq;
using System.Reflection;
+using OpenQA.Selenium.Appium.Service;
+using OpenQA.Selenium.Appium.Service.Options;
namespace Bellatrix.Desktop;
public class App : IDisposable
{
- // TODO: Change to be ThreadLocal.
- private static bool _shouldStartLocalService;
- private static Process _winAppDriverProcess;
-
public App()
{
- _shouldStartLocalService = ConfigurationService.GetSection().ExecutionSettings.ShouldStartLocalService;
ServicesCollection.Main.RegisterInstance(AppService);
}
+ private static readonly bool ShouldStartLocalService = ConfigurationService.GetSection().ExecutionSettings.ShouldStartLocalService;
+ private static Process _appiumServerProcess;
public AppService AppService => ServicesCollection.Current.Resolve();
public ComponentWaitService Wait => ServicesCollection.Current.Resolve();
@@ -54,39 +53,104 @@ public App()
public AWSServicesFactory AWS => ServicesCollection.Current.Resolve();
public IAssert Assert => ServicesCollection.Current.Resolve();
- public static void StartWinAppDriver()
+ public static void StartAppiumServer()
{
- if (_shouldStartLocalService)
+ if (!ShouldStartLocalService)
{
- int port = int.Parse(ConfigurationService.GetSection().ExecutionSettings.Url.Split(':').Last());
+ return;
+ }
+
+ var uri = new Uri(ConfigurationService.GetSection().ExecutionSettings.Url);
+
+ var appiumPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "npm");
+ if (!Directory.Exists(appiumPath))
+ {
+ throw new ArgumentException("Node.js is not installed on the machine. To use BELLATRIX Desktop libraries you need to install it first. You can download it from here: https://nodejs.org/en/download");
+ }
+
+ var appiumPs1Path = Path.Combine(appiumPath, "appium.ps1");
- // Anton(06.09.2018): maybe we can kill WinAppDriver every time
- if (ProcessProvider.IsProcessWithNameRunning("WinAppDriver") || ProcessProvider.IsPortBusy(port))
+ // Minimum Appium Version Check
+ var process = new Process
+ {
+ StartInfo = new ProcessStartInfo
{
- return;
+ FileName = "powershell.exe",
+ Arguments = $"-NoProfile -ExecutionPolicy RemoteSigned -File \"{appiumPs1Path}\" -v",
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
}
+ };
+
+ process.Start();
+ var output = process.StandardOutput.ReadToEnd().Trim();
+ process.WaitForExit();
+
+ if (Version.TryParse(output, out var version) && version < new Version(3, 1, 0))
+ {
+ throw new ArgumentException("Appium version 3.1.0 or higher is required. Please update Appium by running: npm install -g appium@latest");
+ }
+
+ const string latestVersion = "1.2.0-preview.2";
- string winAppDriverPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Windows Application Driver");
- if (!Directory.Exists(winAppDriverPath))
+ process = new Process
+ {
+ StartInfo = new ProcessStartInfo
{
- throw new ArgumentException("Windows Application Driver is not installed on the machine. To use BELLATRIX Desktop libraries you need to install it first. You can download it from here: https://github.com/Microsoft/WinAppDriver/releases");
+ FileName = "powershell.exe",
+ Arguments = $"-NoProfile -ExecutionPolicy RemoteSigned -File \"{appiumPs1Path}\" driver list --installed --json",
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
}
+ };
- string winAppDriverExePath = Path.Combine(winAppDriverPath, "WinAppDriver.exe");
- _winAppDriverProcess = ProcessProvider.StartProcess(winAppDriverExePath, winAppDriverPath, $"{ConfigurationService.GetSection().ExecutionSettings.Url}", true);
- ProcessProvider.WaitPortToGetBusy(port);
- }
- }
+ process.Start();
+ output = process.StandardOutput.ReadToEnd();
+ process.WaitForExit();
- public static void StopWinAppDriver()
- {
- if (_shouldStartLocalService)
+ var json = System.Text.Json.JsonDocument.Parse(output);
+ // TODO: remove latestVersion when Appium 3 version is officially out
+ if (!json.RootElement.TryGetProperty("novawindows", out var driver))
{
- if (ProcessProvider.IsProcessWithNameRunning("WinAppDriver"))
+ Console.WriteLine("NovaWindows driver not found. Installing...");
+ Process.Start(
+ "powershell.exe",
+ $"-NoProfile -ExecutionPolicy RemoteSigned -File \"{appiumPs1Path}\" driver install --source=npm appium-novawindows-driver@{latestVersion}"
+ )?.WaitForExit();
+ }
+ else
+ {
+ var installedVersion = driver.GetProperty("version").GetString() ?? "";
+ if (installedVersion != latestVersion)
{
- ProcessProvider.CloseProcess(_winAppDriverProcess);
+ Console.WriteLine($"Updating NovaWindows driver to {latestVersion}...");
+ Process.Start(
+ "powershell.exe",
+ $"-NoProfile -ExecutionPolicy RemoteSigned -File \"{appiumPs1Path}\" driver update novawindows"
+ )?.WaitForExit();
+ }
+ else
+ {
+ Console.WriteLine("NovaWindows driver is up to date.");
}
}
+
+ _appiumServerProcess = ProcessProvider.StartProcess(
+ "powershell.exe",
+ Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
+ $"-NoProfile -ExecutionPolicy RemoteSigned -File \"{appiumPs1Path}\" -a {uri.Host} -p {uri.Port} --allow-insecure=novawindows:power_shell",
+ true);
+
+ ProcessProvider.WaitPortToGetBusy(uri.Port);
+ }
+
+ public static void StopAppiumServer()
+ {
+ ProcessProvider.CloseProcess(_appiumServerProcess);
}
public void AddAdditionalCapability(string name, object value)
@@ -101,14 +165,14 @@ public void AddElementEventHandler()
where TComponentsEventHandler : ComponentEventHandlers
{
var elementEventHandler = (TComponentsEventHandler)Activator.CreateInstance(typeof(TComponentsEventHandler));
- elementEventHandler.SubscribeToAll();
+ elementEventHandler?.SubscribeToAll();
}
public void RemoveElementEventHandler()
where TComponentsEventHandler : ComponentEventHandlers
{
var elementEventHandler = (TComponentsEventHandler)Activator.CreateInstance(typeof(TComponentsEventHandler));
- elementEventHandler.UnsubscribeToAll();
+ elementEventHandler?.UnsubscribeToAll();
}
public void AddPlugin()
@@ -149,4 +213,4 @@ private string DetermineTestClassFullNameAttributes()
return fullClassName;
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/infrastructure/TestExecutionEngine.cs b/src/Bellatrix.Desktop/infrastructure/TestExecutionEngine.cs
index 3fc11b309..8d8dbf85e 100644
--- a/src/Bellatrix.Desktop/infrastructure/TestExecutionEngine.cs
+++ b/src/Bellatrix.Desktop/infrastructure/TestExecutionEngine.cs
@@ -15,6 +15,7 @@
using Bellatrix.Desktop.Configuration;
using Bellatrix.Desktop.Services;
using OpenQA.Selenium;
+using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop;
@@ -26,12 +27,12 @@ public void StartApp(AppInitializationInfo appConfiguration, ServicesCollection
try
{
var wrappedWebDriver = WrappedWebDriverCreateService.Create(appConfiguration, childContainer);
- childContainer.RegisterInstance>(wrappedWebDriver);
+ childContainer.RegisterInstance(wrappedWebDriver);
////childContainer.RegisterInstance(new AppService(wrappedWebDriver));
////childContainer.RegisterInstance(new ComponentCreateService());
childContainer.RegisterNull();
childContainer.RegisterNull();
- childContainer.RegisterNull();
+ childContainer.RegisterNull();
IsAppStartedCorrectly = true;
}
catch (Exception e)
@@ -50,7 +51,7 @@ public void DisposeAll()
{
foreach (var childContainer in ServicesCollection.Current.GetChildServicesCollections())
{
- var driver = childContainer.Resolve>();
+ var driver = childContainer.Resolve();
DisposeDriverService.Dispose(driver, childContainer);
}
}
diff --git a/src/Bellatrix.Desktop/llm/FindByPrompt.cs b/src/Bellatrix.Desktop/llm/FindByPrompt.cs
index d2e74e645..3145f0030 100644
--- a/src/Bellatrix.Desktop/llm/FindByPrompt.cs
+++ b/src/Bellatrix.Desktop/llm/FindByPrompt.cs
@@ -26,6 +26,7 @@
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
+using OpenQA.Selenium;
namespace Bellatrix.Desktop.LLM;
@@ -38,6 +39,8 @@ public class FindByPrompt : FindStrategy
{
private bool _tryResolveFromPages = true;
+ private WindowsDriver WrappedDriver => ServicesCollection.Current.Resolve();
+
///
/// Initializes a new instance of the class with the specified prompt value.
///
@@ -49,43 +52,23 @@ public FindByPrompt(string value, bool tryResolveFromPages = true) : base(value)
}
///
- /// Locates a single WindowsElement using the resolved XPath.
- ///
- public override WindowsElement FindElement(WindowsDriver driver)
- {
- var location = driver.CurrentWindowHandle;
- var xpath = ResolveLocator(location, driver);
- return driver.FindElementByXPath(xpath);
- }
-
- ///
- /// Locates all matching WindowsElements using the resolved XPath.
- ///
- public override IEnumerable FindAllElements(WindowsDriver driver)
- {
- var location = driver.CurrentWindowHandle;
- var xpath = ResolveLocator(location, driver);
- return driver.FindElementsByXPath(xpath);
- }
-
- ///
- /// Locates a single AppiumWebElement in the context of a parent element using the resolved XPath.
+ /// Locates a single AppiumElement in the context of a parent element using the resolved XPath.
///
- public override AppiumWebElement FindElement(WindowsElement element)
+ public override AppiumElement FindElement(ISearchContext searchContext)
{
- var location = element.WrappedDriver.CurrentWindowHandle;
- var xpath = ResolveLocator(location, element.WrappedDriver as WindowsDriver);
- return element.FindElementByXPath(xpath);
+ var location = WrappedDriver.CurrentWindowHandle;
+ var xpath = ResolveLocator(location, WrappedDriver as WindowsDriver);
+ return searchContext.FindElement(By.XPath(xpath)) as AppiumElement;
}
///
- /// Locates all matching AppiumWebElements in the context of a parent element using the resolved XPath.
+ /// Locates all matching AppiumElements in the context of a parent element using the resolved XPath.
///
- public override IEnumerable FindAllElements(WindowsElement element)
+ public override IEnumerable FindAllElements(ISearchContext searchContext)
{
- var location = element.WrappedDriver.CurrentWindowHandle;
- var xpath = ResolveLocator(location, element.WrappedDriver as WindowsDriver);
- return element.FindElementsByXPath(xpath);
+ var location = WrappedDriver.CurrentWindowHandle;
+ var xpath = ResolveLocator(location, WrappedDriver as WindowsDriver);
+ return searchContext.FindElements(By.XPath(xpath)).Select(el => el as AppiumElement);
}
///
@@ -94,7 +77,7 @@ public override IEnumerable FindAllElements(WindowsElement ele
/// Desktop app window handle or location identifier.
/// WindowsDriver for presence checking.
/// A valid XPath string.
- private string ResolveLocator(string location, WindowsDriver driver)
+ private string ResolveLocator(string location, WindowsDriver driver)
{
// Step 1: Try RAG memory (PageObjects index)
if (_tryResolveFromPages)
@@ -129,7 +112,7 @@ private string ResolveLocator(string location, WindowsDriver dri
/// The natural language instruction for the element.
/// WindowsDriver for presence checking.
/// XPath string if found; otherwise null.
- private string TryResolveFromPageObjectMemory(string instruction, WindowsDriver driver)
+ private string TryResolveFromPageObjectMemory(string instruction, WindowsDriver driver)
{
var match = SemanticKernelService.Memory
.SearchAsync(instruction, index: "PageObjects", limit: 1)
@@ -160,7 +143,7 @@ private string TryResolveFromPageObjectMemory(string instruction, WindowsDriver<
/// WindowsDriver for presence checking.
/// Maximum number of attempts to generate a working selector.
/// XPath string if found; otherwise throws InvalidOperationException.
- private string ResolveViaPromptFallback(string location, WindowsDriver driver, int maxAttempts = 3)
+ private string ResolveViaPromptFallback(string location, WindowsDriver driver, int maxAttempts = 3)
{
var viewSnapshotProvider = ServicesCollection.Current.Resolve();
var failedSelectors = new List();
@@ -222,9 +205,9 @@ private static string ParsePromptLocatorToXPath(string promptResult)
/// The WindowsDriver instance.
/// The XPath locator to check.
/// true if at least one element is found; otherwise false.
- private static bool IsElementPresent(WindowsDriver driver, string xpath)
+ private static bool IsElementPresent(WindowsDriver driver, string xpath)
{
- try { return driver.FindElementsByXPath(xpath).Any(); }
+ try { return driver.FindElements(By.XPath(xpath)).Any(); }
catch { return false; }
}
diff --git a/src/Bellatrix.Desktop/plugins/execution/AppLifecyclePlugin.cs b/src/Bellatrix.Desktop/plugins/execution/AppLifecyclePlugin.cs
index 4a8d13826..13448ea52 100644
--- a/src/Bellatrix.Desktop/plugins/execution/AppLifecyclePlugin.cs
+++ b/src/Bellatrix.Desktop/plugins/execution/AppLifecyclePlugin.cs
@@ -14,7 +14,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
-using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Bellatrix.Desktop.Configuration;
@@ -23,7 +22,6 @@
using Bellatrix.Plugins;
using Bellatrix.Utilities;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Remote;
namespace Bellatrix.Desktop.Plugins;
@@ -108,7 +106,10 @@ private void RestartApp(ServicesCollection container)
{
var currentAppConfiguration = container.Resolve("_currentAppConfiguration");
- ShutdownApp(container);
+ if (currentAppConfiguration.AppPath != "Root")
+ {
+ ShutdownApp(container);
+ }
// Register the ExecutionEngine that should be used for the current run. Will be used in the next test as PreviousEngineType.
var testExecutionEngine = new TestExecutionEngine();
@@ -167,7 +168,7 @@ private AppInitializationInfo GetCurrentAppConfiguration(MemberInfo memberInfo,
AppPath = ConfigurationService.GetSection().ExecutionSettings.DefaultAppPath,
Lifecycle = currentLifecycle,
Size = currentAppSize,
- AppiumOptions = new DesiredCapabilities(),
+ AppiumOptions = new AppiumOptions(),
ClassFullName = testClassType.FullName,
};
@@ -218,58 +219,74 @@ private void InitializeCustomCodeOptions(dynamic options, Type testClassType)
private void InitializeGridOptionsFromConfiguration(dynamic options, Type testClassType)
{
- if (ConfigurationService.GetSection().ExecutionSettings.Arguments == null)
+ var executionSettings = ConfigurationService.GetSection().ExecutionSettings;
+
+ if (executionSettings.Arguments == null)
+ {
+ return;
+ }
+
+ if (executionSettings.Arguments.Count <= 0)
{
return;
}
- if (ConfigurationService.GetSection().ExecutionSettings.Arguments[0].Count > 0)
+ if (executionSettings.Arguments[0].Count <= 0)
{
- foreach (var item in ConfigurationService.GetSection().ExecutionSettings.Arguments[0])
+ return;
+ }
+
+ foreach (var item in executionSettings.Arguments[0])
+ {
+ if (!string.IsNullOrEmpty(item.Key) && item.Value != null)
{
- if (!string.IsNullOrEmpty(item.Key) && !string.IsNullOrEmpty(item.Value))
- {
- options.AddAdditionalCapability(item.Key, FormatGridOptions(item.Value, testClassType));
- }
+ options.AddAdditionalCapability(item.Key, FormatGridOptions(item.Value, testClassType));
}
}
}
- private dynamic FormatGridOptions(string option, Type testClassType)
+ private dynamic FormatGridOptions(object option, Type testClassType)
{
- if (bool.TryParse(option, out bool result))
+ if (option is not string)
+ {
+ return option;
+ }
+
+ if (bool.TryParse((string)option, out bool result))
{
return result;
}
- else if (int.TryParse(option, out int resultNumber))
+
+ if (int.TryParse((string)option, out int resultNumber))
{
return resultNumber;
}
- else if (option.StartsWith("env_") || option.StartsWith("vault_"))
+
+ if (((string)option).StartsWith("env_") || ((string)option).StartsWith("vault_"))
{
- return SecretsResolver.GetSecret(() => option);
+ return SecretsResolver.GetSecret(() => (string)option);
}
- else if (double.TryParse(option, out double resultRealNumber))
+
+ if (double.TryParse((string)option, out double resultRealNumber))
{
return resultRealNumber;
}
- else if (option.StartsWith("AssemblyFolder", StringComparison.Ordinal))
+
+ if (((string)option).StartsWith("AssemblyFolder", StringComparison.Ordinal))
{
var executionFolder = ExecutionDirectoryResolver.GetDriverExecutablePath();
- option = option.Replace("AssemblyFolder", executionFolder);
+ option = ((string)option).Replace("AssemblyFolder", executionFolder);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
- option = option.Replace('\\', '/');
+ option = ((string)option).Replace('\\', '/');
}
return option;
}
- else
- {
- var runName = testClassType.Assembly.GetName().Name;
- var timestamp = $"{DateTime.Now:yyyyMMdd.HHmm}";
- return option.Replace("{runName}", timestamp).Replace("{runName}", runName);
- }
+
+ var runName = testClassType.Assembly.GetName().Name;
+ var timestamp = $"{DateTime.Now:yyyyMMdd.HHmm}";
+ return ((string)option).Replace("{runName}", timestamp).Replace("{runName}", runName);
}
}
diff --git a/src/Bellatrix.Desktop/plugins/execution/Attributes/AppAttribute.cs b/src/Bellatrix.Desktop/plugins/execution/Attributes/AppAttribute.cs
index 6cdd34eaf..0347a6419 100644
--- a/src/Bellatrix.Desktop/plugins/execution/Attributes/AppAttribute.cs
+++ b/src/Bellatrix.Desktop/plugins/execution/Attributes/AppAttribute.cs
@@ -12,12 +12,10 @@
// Anton Angelov
// https://bellatrix.solutions/
using System;
-using System.Collections.Generic;
using System.Drawing;
using Bellatrix.Desktop.Configuration;
using Bellatrix.Desktop.Services;
using OpenQA.Selenium.Appium;
-using OpenQA.Selenium.Remote;
namespace Bellatrix.Desktop;
@@ -26,11 +24,13 @@ public class AppAttribute : Attribute
{
public AppAttribute(string appPath, Lifecycle lifecycle = Lifecycle.NotSet)
{
- AppConfiguration = new AppInitializationInfo();
- AppConfiguration.AppPath = appPath;
- AppConfiguration.Lifecycle = lifecycle;
- AppConfiguration.Size = default;
- AppConfiguration.AppiumOptions = new DesiredCapabilities();
+ AppConfiguration = new AppInitializationInfo
+ {
+ AppPath = appPath,
+ Lifecycle = lifecycle,
+ Size = default,
+ AppiumOptions = new AppiumOptions()
+ };
}
public AppAttribute(string appPath, int width, int height, Lifecycle behavior = Lifecycle.NotSet)
diff --git a/src/Bellatrix.Desktop/screenshots/VanillaWebDriverScreenshotEngine.cs b/src/Bellatrix.Desktop/screenshots/VanillaWebDriverScreenshotEngine.cs
index a8db6b04b..b4f159f7f 100644
--- a/src/Bellatrix.Desktop/screenshots/VanillaWebDriverScreenshotEngine.cs
+++ b/src/Bellatrix.Desktop/screenshots/VanillaWebDriverScreenshotEngine.cs
@@ -29,7 +29,7 @@ public string TakeScreenshot(ServicesCollection serviceContainer)
public string TakeScreenshotVanillaWebDriver(ServicesCollection serviceContainer)
{
- var driver = serviceContainer.Resolve>();
+ var driver = serviceContainer.Resolve();
Screenshot screenshot = ((ITakesScreenshot)driver).GetScreenshot();
return screenshot.AsBase64EncodedString;
}
diff --git a/src/Bellatrix.Desktop/services/AppService.cs b/src/Bellatrix.Desktop/services/AppService.cs
index dffda5fde..354cec40b 100644
--- a/src/Bellatrix.Desktop/services/AppService.cs
+++ b/src/Bellatrix.Desktop/services/AppService.cs
@@ -22,7 +22,7 @@ namespace Bellatrix.Desktop.Services;
public class AppService : DesktopService, IViewSnapshotProvider
{
- public AppService(WindowsDriver wrappedDriver)
+ public AppService(WindowsDriver wrappedDriver)
: base(wrappedDriver)
{
}
diff --git a/src/Bellatrix.Desktop/services/ComponentsRepository.cs b/src/Bellatrix.Desktop/services/ComponentsRepository.cs
index af19ae032..23f429747 100644
--- a/src/Bellatrix.Desktop/services/ComponentsRepository.cs
+++ b/src/Bellatrix.Desktop/services/ComponentsRepository.cs
@@ -16,33 +16,34 @@
using System.Reflection;
using Bellatrix.Desktop.Locators;
using Bellatrix.Desktop.PageObjects;
+using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
namespace Bellatrix.Desktop.Services;
-internal class ComponentsRepository
+public class ComponentsRepository
{
- public dynamic CreateComponentWithParent(FindStrategy by, WindowsElement parenTComponent, Type newElementType)
+ public dynamic CreateComponentWithParent(FindStrategy by, AppiumElement parentElement, Type newElementType)
{
DetermineComponentAttributes(out var elementName, out var pageName);
dynamic element = Activator.CreateInstance(newElementType);
element.By = by;
- element.ParentWrappedElement = parenTComponent;
- element.ElementName = string.IsNullOrEmpty(elementName) ? $"control ({by})" : elementName;
+ element.ParentWrappedElement = parentElement;
+ ////element.ElementName = string.IsNullOrEmpty(elementName) ? $"control ({by})" : elementName; // temporary, revert
element.PageName = pageName ?? string.Empty;
return element;
}
- public TComponentType CreateComponentWithParent(FindStrategy by, WindowsElement parenTComponent, WindowsElement foundElement, int elementsIndex)
+ public TComponentType CreateComponentWithParent(FindStrategy by, AppiumElement parentElement, AppiumElement foundElement, int elementsIndex)
where TComponentType : Component
{
DetermineComponentAttributes(out var elementName, out var pageName);
var element = Activator.CreateInstance();
element.By = by;
- element.ParentWrappedElement = parenTComponent;
+ element.ParentWrappedElement = parentElement;
element.WrappedElement = foundElement;
element.FoundWrappedElement = foundElement;
element.ElementIndex = elementsIndex;
@@ -52,7 +53,7 @@ public TComponentType CreateComponentWithParent(FindStrategy by,
return element;
}
- public dynamic CreateComponentThatIsFound(FindStrategy by, WindowsElement webElement, Type newElementType)
+ public dynamic CreateComponentThatIsFound(FindStrategy by, AppiumElement webElement, Type newElementType)
{
DetermineComponentAttributes(out var elementName, out var pageName);
@@ -66,7 +67,7 @@ public dynamic CreateComponentThatIsFound(FindStrategy by, WindowsElement webEle
return element;
}
- public TComponentType CreateComponentThatIsFound(FindStrategy by, WindowsElement webElement)
+ public TComponentType CreateComponentThatIsFound(FindStrategy by, AppiumElement webElement)
where TComponentType : Component
{
DetermineComponentAttributes(out var elementName, out var pageName);
diff --git a/src/Bellatrix.Desktop/services/DesktopService.cs b/src/Bellatrix.Desktop/services/DesktopService.cs
index 2bd6a151c..c60d8b295 100644
--- a/src/Bellatrix.Desktop/services/DesktopService.cs
+++ b/src/Bellatrix.Desktop/services/DesktopService.cs
@@ -17,10 +17,10 @@ namespace Bellatrix.Desktop.Services;
public abstract class DesktopService
{
- protected DesktopService(WindowsDriver wrappedDriver)
+ protected DesktopService(WindowsDriver wrappedDriver)
{
WrappedDriver = wrappedDriver;
}
- public WindowsDriver WrappedDriver { get; set; }
+ public WindowsDriver WrappedDriver { get; set; }
}
diff --git a/src/Bellatrix.Desktop/services/DisposeDriverService.cs b/src/Bellatrix.Desktop/services/DisposeDriverService.cs
index 9d27a5239..67df4b4f4 100644
--- a/src/Bellatrix.Desktop/services/DisposeDriverService.cs
+++ b/src/Bellatrix.Desktop/services/DisposeDriverService.cs
@@ -17,24 +17,24 @@ namespace Bellatrix.Desktop.Services;
public static class DisposeDriverService
{
- public static void Dispose(WindowsDriver driver, ServicesCollection childContainer)
+ public static void Dispose(WindowsDriver driver, ServicesCollection childContainer)
{
driver?.Quit();
driver?.Dispose();
- childContainer.UnregisterSingleInstance>();
+ childContainer.UnregisterSingleInstance();
}
public static void Dispose(ServicesCollection childContainer)
{
try
{
- if (childContainer.IsRegistered>())
+ if (childContainer.IsRegistered())
{
- var webDriver = childContainer.Resolve>();
+ var webDriver = childContainer.Resolve();
webDriver?.Quit();
webDriver?.Dispose();
- childContainer.UnregisterSingleInstance>();
- ServicesCollection.Main.UnregisterSingleInstance>();
+ childContainer.UnregisterSingleInstance();
+ ServicesCollection.Main.UnregisterSingleInstance();
}
}
catch (System.Exception ex)
@@ -49,10 +49,10 @@ public static void DisposeAll()
{
try
{
- var driver = childContainer.Resolve>();
+ var driver = childContainer.Resolve();
driver?.Quit();
driver?.Dispose();
- childContainer?.UnregisterSingleInstance>();
+ childContainer?.UnregisterSingleInstance();
}
catch (System.Exception ex)
{
@@ -60,6 +60,6 @@ public static void DisposeAll()
}
}
- ServicesCollection.Main.UnregisterSingleInstance>();
+ ServicesCollection.Main.UnregisterSingleInstance();
}
}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/services/WrappedWebDriverCreateService.cs b/src/Bellatrix.Desktop/services/WrappedWebDriverCreateService.cs
index 273a9210d..2fe6be6f7 100644
--- a/src/Bellatrix.Desktop/services/WrappedWebDriverCreateService.cs
+++ b/src/Bellatrix.Desktop/services/WrappedWebDriverCreateService.cs
@@ -15,48 +15,86 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
+using Amazon.Runtime.Internal.Transform;
using Bellatrix.Desktop.Configuration;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
-using OpenQA.Selenium.Remote;
namespace Bellatrix.Desktop.Services;
public class WrappedWebDriverCreateService
{
- private static readonly string _serviceUrl;
+ private static readonly string ServiceUrl;
+
+ private const string CloseButtonXPath = "//Button[contains(translate(@AutomationId, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'close') or contains(translate(@Name, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'close')]";
static WrappedWebDriverCreateService()
{
- _serviceUrl = ConfigurationService.GetSection().ExecutionSettings.Url;
+ ServiceUrl = ConfigurationService.GetSection().ExecutionSettings.Url;
}
- public static WindowsDriver Create(AppInitializationInfo appConfiguration, ServicesCollection childContainer)
+ public static WindowsDriver Create(AppInitializationInfo appConfiguration, ServicesCollection childContainer)
{
- var driverOptions = childContainer.Resolve(appConfiguration.ClassFullName) ?? childContainer.Resolve() ?? appConfiguration.AppiumOptions;
- driverOptions.SetCapability("app", appConfiguration.AppPath);
- driverOptions.SetCapability("deviceName", "WindowsPC");
- driverOptions.SetCapability("platformName", "Windows");
- string workingDir = Path.GetDirectoryName(appConfiguration.AppPath);
- driverOptions.SetCapability("appWorkingDir", workingDir);
- driverOptions.SetCapability("createSessionTimeout", ConfigurationService.GetSection().TimeoutSettings.CreateSessionTimeout);
- driverOptions.SetCapability("ms:waitForAppLaunch", ConfigurationService.GetSection().TimeoutSettings.WaitForAppLaunchTimeout);
-
- var additionalCapabilities = ServicesCollection.Main.Resolve>($"caps-{appConfiguration.ClassFullName}") ?? new Dictionary();
+ var driverOptions = childContainer.Resolve(appConfiguration.ClassFullName) ?? childContainer.Resolve() ?? appConfiguration.AppiumOptions;
+
+ var appiumOptions = new Dictionary
+ {
+ { "appWorkingDir", Path.GetDirectoryName(appConfiguration.AppPath) },
+ { "createSessionTimeout", ConfigurationService.GetSection().TimeoutSettings.CreateSessionTimeout },
+ { "ms:waitForAppLaunch", ConfigurationService.GetSection().TimeoutSettings.WaitForAppLaunchTimeout },
+ { "ms:experimental-webdriver", true }
+ };
+
+ appiumOptions.Add("automationName", "NovaWindows");
+
+ if (appConfiguration.AppPath == "Root")
+ {
+ if (appConfiguration.WindowHandle == null)
+ {
+ appiumOptions.Add("app", appConfiguration.AppPath);
+ }
+ else
+ {
+ appiumOptions.Add("appTopLevelWindow", appConfiguration.WindowHandle);
+ }
+ }
+ else
+ {
+ appiumOptions.Add("app", appConfiguration.AppPath);
+ }
+
+ var appiumCapabilities = ServicesCollection.Main.Resolve>($"caps-{appConfiguration.ClassFullName}");
+
+ driverOptions.PlatformName = "Windows";
+ driverOptions.AddAdditionalAppiumOption("appium:options", appiumOptions);
+ var additionalCapabilities = appiumCapabilities ?? new Dictionary();
foreach (var additionalCapability in additionalCapabilities)
{
- driverOptions.SetCapability(additionalCapability.Key, additionalCapability.Value);
+ driverOptions.AddAdditionalAppiumOption(additionalCapability.Key, additionalCapability.Value);
}
- var wrappedWebDriver = new WindowsDriver(new Uri(_serviceUrl), driverOptions);
+ var wrappedWebDriver = new WindowsDriver(new Uri(ServiceUrl), driverOptions);
wrappedWebDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(ConfigurationService.GetSection().TimeoutSettings.ImplicitWaitTimeout);
- ChangeWindowSize(appConfiguration.Size, wrappedWebDriver);
- wrappedWebDriver.SwitchTo().Window(wrappedWebDriver.CurrentWindowHandle);
+ if (!appiumOptions.TryGetValue("app", out var app) || app as string == "Root")
+ {
+ return wrappedWebDriver;
+ }
+
try
{
- var closeButton = wrappedWebDriver.FindElementByAccessibilityId("Close");
- wrappedWebDriver.Mouse.MouseMove(closeButton.Coordinates);
+ var closeButton = wrappedWebDriver.FindElement(By.XPath(CloseButtonXPath));
+
+ wrappedWebDriver.ExecuteScript("windows: hover", new Dictionary
+ {
+ { "startElementId", closeButton.Id },
+ { "endElementId", closeButton.Id },
+ { "durationMs", 0 }
+ });
+
+ wrappedWebDriver.SwitchTo().Window(wrappedWebDriver.CurrentWindowHandle);
}
catch (Exception e)
{
@@ -66,7 +104,7 @@ public static WindowsDriver Create(AppInitializationInfo appConf
return wrappedWebDriver;
}
- private static void ChangeWindowSize(Size windowSize, WindowsDriver wrappedWebDriver)
+ private static void ChangeWindowSize(Size windowSize, WindowsDriver wrappedWebDriver)
{
try
{
@@ -85,4 +123,4 @@ private static void ChangeWindowSize(Size windowSize, WindowsDriver NormalizeAppPath(); set => _appPath = value; }
+
+ public string WindowHandle { get; set; }
- public DesiredCapabilities AppiumOptions { get; set; }
+ public AppiumOptions AppiumOptions { get; set; }
public bool Equals(AppInitializationInfo other)
{
@@ -64,11 +66,18 @@ private string NormalizeAppPath()
{
return _appPath;
}
- else if (_appPath.StartsWith("AssemblyFolder", StringComparison.Ordinal))
+
+ if (_appPath.StartsWith("AssemblyFolder", StringComparison.Ordinal))
{
var executionFolder = ExecutionDirectoryResolver.GetDriverExecutablePath();
_appPath = _appPath.Replace("AssemblyFolder", executionFolder);
}
+
+ if (_appPath.StartsWith("UserFolder", StringComparison.Ordinal))
+ {
+ var executionFolder = ExecutionDirectoryResolver.GetDriverExecutablePath();
+ _appPath = _appPath.Replace("UserFolder", Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
+ }
return _appPath;
}
diff --git a/src/Bellatrix.Desktop/settings/ExecutionSettings.cs b/src/Bellatrix.Desktop/settings/ExecutionSettings.cs
index 8524497d6..cd8eae9a8 100644
--- a/src/Bellatrix.Desktop/settings/ExecutionSettings.cs
+++ b/src/Bellatrix.Desktop/settings/ExecutionSettings.cs
@@ -23,6 +23,7 @@ public class ExecutionSettings
public string Resolution { get; set; }
public bool ShouldStartLocalService { get; set; } = true;
public string Url { get; set; }
- public List> Arguments { get; set; }
+ public List> Arguments { get; set; }
+
public bool IsCloudRun { get; set; }
}
\ No newline at end of file
diff --git a/src/Bellatrix.Desktop/validators/ValidateControlExtensions.cs b/src/Bellatrix.Desktop/validators/ValidateControlExtensions.cs
index 206214e54..6461df056 100644
--- a/src/Bellatrix.Desktop/validators/ValidateControlExtensions.cs
+++ b/src/Bellatrix.Desktop/validators/ValidateControlExtensions.cs
@@ -26,7 +26,7 @@ private static void WaitUntil(Func waitCondition, string exceptionMessage,
{
var localTimeout = timeoutInSeconds ?? ConfigurationService.GetSection().TimeoutSettings.ValidationsTimeout;
var localSleepInterval = sleepIntervalInSeconds ?? ConfigurationService.GetSection().TimeoutSettings.SleepInterval;
- var wrappedWebDriver = ServicesCollection.Current.Resolve>();
+ var wrappedWebDriver = ServicesCollection.Current.Resolve();
var webDriverWait = new WebDriverWait(new SystemClock(), wrappedWebDriver, TimeSpan.FromSeconds(localTimeout), TimeSpan.FromSeconds(localSleepInterval));
webDriverWait.IgnoreExceptionTypes(typeof(NoSuchElementException), typeof(StaleElementReferenceException));
bool LocalCondition(IWebDriver s)
diff --git a/src/Bellatrix.Desktop/waitstrategies/WaitNotBeVisibleStrategy.cs b/src/Bellatrix.Desktop/waitstrategies/WaitNotBeVisibleStrategy.cs
index 2ecdda5c3..768a7bdc4 100644
--- a/src/Bellatrix.Desktop/waitstrategies/WaitNotBeVisibleStrategy.cs
+++ b/src/Bellatrix.Desktop/waitstrategies/WaitNotBeVisibleStrategy.cs
@@ -31,7 +31,7 @@ public override void WaitUntil(TBy by)
WaitUntil(d => ElementIsInvisible(WrappedWebDriver, by), TimeoutInterval, SleepInterval);
}
- private bool ElementIsInvisible(WindowsDriver searchContext, TBy by)
+ private bool ElementIsInvisible(WindowsDriver searchContext, TBy by)
where TBy : Locators.FindStrategy
{
try
diff --git a/src/Bellatrix.Desktop/waitstrategies/WaitNotExistStrategy.cs b/src/Bellatrix.Desktop/waitstrategies/WaitNotExistStrategy.cs
index f15d9d752..3bdec064c 100644
--- a/src/Bellatrix.Desktop/waitstrategies/WaitNotExistStrategy.cs
+++ b/src/Bellatrix.Desktop/waitstrategies/WaitNotExistStrategy.cs
@@ -32,7 +32,7 @@ public override void WaitUntil(TBy by)
WaitUntil(d => ElementNotExists(WrappedWebDriver, by), TimeoutInterval, SleepInterval);
}
- private bool ElementNotExists(WindowsDriver searchContext, TBy by)
+ private bool ElementNotExists(WindowsDriver searchContext, TBy by)
where TBy : FindStrategy
{
try
diff --git a/src/Bellatrix.Desktop/waitstrategies/WaitStrategy.cs b/src/Bellatrix.Desktop/waitstrategies/WaitStrategy.cs
index affb7fed6..9666e3f60 100644
--- a/src/Bellatrix.Desktop/waitstrategies/WaitStrategy.cs
+++ b/src/Bellatrix.Desktop/waitstrategies/WaitStrategy.cs
@@ -23,12 +23,12 @@ public abstract class WaitStrategy
{
protected WaitStrategy(int? timeoutInterval = null, int? sleepInterval = null)
{
- WrappedWebDriver = ServicesCollection.Current.Resolve>();
+ WrappedWebDriver = ServicesCollection.Current.Resolve();
TimeoutInterval = timeoutInterval;
SleepInterval = sleepInterval ?? ConfigurationService.GetSection().TimeoutSettings.SleepInterval;
}
- protected WindowsDriver WrappedWebDriver { get; }
+ protected WindowsDriver WrappedWebDriver { get; }
protected int? TimeoutInterval { get; set; }
@@ -61,7 +61,7 @@ protected void WaitUntil(Func waitCondition, int? timeo
}
}
- protected IWebElement FindElement(WindowsDriver searchContext, TBy by)
+ protected IWebElement FindElement(WindowsDriver searchContext, TBy by)
where TBy : Locators.FindStrategy
{
var element = by.FindElement(searchContext);
diff --git a/src/Bellatrix.Desktop/waitstrategies/WaitToBeClickable.cs b/src/Bellatrix.Desktop/waitstrategies/WaitToBeClickable.cs
index 3f623fe13..c82ca6666 100644
--- a/src/Bellatrix.Desktop/waitstrategies/WaitToBeClickable.cs
+++ b/src/Bellatrix.Desktop/waitstrategies/WaitToBeClickable.cs
@@ -31,7 +31,7 @@ public override void WaitUntil(TBy by)
WaitUntil(d => ElementIsClickable(WrappedWebDriver, by), TimeoutInterval, SleepInterval);
}
- private bool ElementIsClickable(WindowsDriver searchContext, TBy by)
+ private bool ElementIsClickable(WindowsDriver searchContext, TBy by)
where TBy : Locators.FindStrategy
{
var element = by.FindElement(searchContext);
diff --git a/src/Bellatrix.Desktop/waitstrategies/WaitToBeVisibleStrategy.cs b/src/Bellatrix.Desktop/waitstrategies/WaitToBeVisibleStrategy.cs
index 7541a3f98..324126212 100644
--- a/src/Bellatrix.Desktop/waitstrategies/WaitToBeVisibleStrategy.cs
+++ b/src/Bellatrix.Desktop/waitstrategies/WaitToBeVisibleStrategy.cs
@@ -31,7 +31,7 @@ public override void WaitUntil(TBy by)
WaitUntil(d => ElementIsVisible(WrappedWebDriver, by), TimeoutInterval, SleepInterval);
}
- private bool ElementIsVisible(WindowsDriver searchContext, TBy by)
+ private bool ElementIsVisible(WindowsDriver searchContext, TBy by)
where TBy : Locators.FindStrategy
{
try
diff --git a/src/Bellatrix.Desktop/waitstrategies/WaitToExistStrategy.cs b/src/Bellatrix.Desktop/waitstrategies/WaitToExistStrategy.cs
index 54f97b078..7d699d8cd 100644
--- a/src/Bellatrix.Desktop/waitstrategies/WaitToExistStrategy.cs
+++ b/src/Bellatrix.Desktop/waitstrategies/WaitToExistStrategy.cs
@@ -32,7 +32,7 @@ public override void WaitUntil(TBy by)
WaitUntil(d => ElementExists(WrappedWebDriver, by), TimeoutInterval, SleepInterval);
}
- private bool ElementExists(WindowsDriver searchContext, TBy by)
+ private bool ElementExists(WindowsDriver searchContext, TBy by)
where TBy : FindStrategy
{
try
diff --git a/src/Bellatrix.Desktop/waitstrategies/WaitToHaveContentStrategy.cs b/src/Bellatrix.Desktop/waitstrategies/WaitToHaveContentStrategy.cs
index 2ecdde92c..27f25d95d 100644
--- a/src/Bellatrix.Desktop/waitstrategies/WaitToHaveContentStrategy.cs
+++ b/src/Bellatrix.Desktop/waitstrategies/WaitToHaveContentStrategy.cs
@@ -31,7 +31,7 @@ public override void WaitUntil(TBy by)
WaitUntil(d => ElementHasContent(WrappedWebDriver, by), TimeoutInterval, SleepInterval);
}
- private bool ElementHasContent(WindowsDriver searchContext, TBy by)
+ private bool ElementHasContent(WindowsDriver searchContext, TBy by)
where TBy : Locators.FindStrategy
{
try
diff --git a/src/Bellatrix.DynamicTestCases/Bellatrix.DynamicTestCases.csproj b/src/Bellatrix.DynamicTestCases/Bellatrix.DynamicTestCases.csproj
index c2da525e3..381fe0d5d 100644
--- a/src/Bellatrix.DynamicTestCases/Bellatrix.DynamicTestCases.csproj
+++ b/src/Bellatrix.DynamicTestCases/Bellatrix.DynamicTestCases.csproj
@@ -8,8 +8,8 @@
-
-
+
+
QTestSDK.dll
diff --git a/src/Bellatrix.Email/Bellatrix.Email.csproj b/src/Bellatrix.Email/Bellatrix.Email.csproj
index bb038b22a..924bf582f 100644
--- a/src/Bellatrix.Email/Bellatrix.Email.csproj
+++ b/src/Bellatrix.Email/Bellatrix.Email.csproj
@@ -7,8 +7,8 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Bellatrix.ImageRecognition/Bellatrix.ImageRecognition.csproj b/src/Bellatrix.ImageRecognition/Bellatrix.ImageRecognition.csproj
index d1d054533..a119c1efe 100644
--- a/src/Bellatrix.ImageRecognition/Bellatrix.ImageRecognition.csproj
+++ b/src/Bellatrix.ImageRecognition/Bellatrix.ImageRecognition.csproj
@@ -1,7 +1,7 @@
-
+
diff --git a/src/Bellatrix.ImageRecognition/Screen.cs b/src/Bellatrix.ImageRecognition/Screen.cs
index d3bd51247..efba8439a 100644
--- a/src/Bellatrix.ImageRecognition/Screen.cs
+++ b/src/Bellatrix.ImageRecognition/Screen.cs
@@ -45,8 +45,15 @@ public bool Exists(string imagePath, double? similarity = null, double? timeoutI
similarity = InitializeSimilarity(similarity);
IImage image = ImageFactory.FromFile(imagePath, similarity);
timeoutInSeconds = InitializeTimeout(timeoutInSeconds);
- _runtime.Run(image.ToSikuliScript("exists", timeoutInSeconds), timeoutInSeconds);
- return true;
+ try
+ {
+ _runtime.Run(image.ToSikuliScript("exists", timeoutInSeconds), timeoutInSeconds);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
}
public void Click(string imagePath, double? similarity = null, double? timeoutInSeconds = null)
@@ -204,4 +211,4 @@ private void InitializeRuntime()
{
return defaultSimilarity ?? _defaultSimilarity;
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.KeyVault/Bellatrix.KeyVault.csproj b/src/Bellatrix.KeyVault/Bellatrix.KeyVault.csproj
index dfce15560..b1bbd34be 100644
--- a/src/Bellatrix.KeyVault/Bellatrix.KeyVault.csproj
+++ b/src/Bellatrix.KeyVault/Bellatrix.KeyVault.csproj
@@ -1,9 +1,9 @@
-
-
-
+
+
+
diff --git a/src/Bellatrix.Layout/Bellatrix.Layout.csproj b/src/Bellatrix.Layout/Bellatrix.Layout.csproj
index ff9cab05b..6b6ac5d49 100644
--- a/src/Bellatrix.Layout/Bellatrix.Layout.csproj
+++ b/src/Bellatrix.Layout/Bellatrix.Layout.csproj
@@ -1,7 +1,7 @@
-
+
diff --git a/src/Bellatrix.Mobile/AndroidPluginsConfiguration.cs b/src/Bellatrix.Mobile/AndroidPluginsConfiguration.cs
index 35c6dab75..e579deee1 100644
--- a/src/Bellatrix.Mobile/AndroidPluginsConfiguration.cs
+++ b/src/Bellatrix.Mobile/AndroidPluginsConfiguration.cs
@@ -159,7 +159,8 @@ public static void ConfigureLLM()
{
if (ConfigurationService.GetSection() == null)
{
- throw new ArgumentException("Could not load LargeLanguageModelsSettings section from testFrameworkSettings.json");
+ Logger.LogError("Could not load LargeLanguageModelsSettings section from testFrameworkSettings.json");
+ return;
}
try
@@ -183,4 +184,4 @@ public static void ConfigureLLM()
Logger.LogError(ex.ToString());
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Mobile/Bellatrix.Mobile.csproj b/src/Bellatrix.Mobile/Bellatrix.Mobile.csproj
index 4575e893b..bdc76dbcf 100644
--- a/src/Bellatrix.Mobile/Bellatrix.Mobile.csproj
+++ b/src/Bellatrix.Mobile/Bellatrix.Mobile.csproj
@@ -1,18 +1,18 @@
- NU1701;NU1702;NU1705;NU1608;NU1605;
+ NU1701;NU1702;NU1705;NU1608;NU1605;MSTEST0001
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/Bellatrix.Mobile/IOSPluginsConfiguration.cs b/src/Bellatrix.Mobile/IOSPluginsConfiguration.cs
index b7dd88747..d56435b8e 100644
--- a/src/Bellatrix.Mobile/IOSPluginsConfiguration.cs
+++ b/src/Bellatrix.Mobile/IOSPluginsConfiguration.cs
@@ -155,7 +155,8 @@ public static void ConfigureLLM()
{
if (ConfigurationService.GetSection() == null)
{
- throw new ArgumentException("Could not load LargeLanguageModelsSettings section from testFrameworkSettings.json");
+ Logger.LogError("Could not load LargeLanguageModelsSettings section from testFrameworkSettings.json");
+ return;
}
try
@@ -179,4 +180,4 @@ public static void ConfigureLLM()
Logger.LogError(ex.ToString());
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Mobile/plugins/execution/AppWorkflowPlugin.cs b/src/Bellatrix.Mobile/plugins/execution/AppWorkflowPlugin.cs
index 663528892..233af0d96 100644
--- a/src/Bellatrix.Mobile/plugins/execution/AppWorkflowPlugin.cs
+++ b/src/Bellatrix.Mobile/plugins/execution/AppWorkflowPlugin.cs
@@ -292,7 +292,7 @@ private TEnum Parse(string value)
return (TEnum)Enum.Parse(typeof(TEnum), value.Replace(" ", string.Empty), true);
}
- private void InitializeCustomCodeOptions(dynamic options, Type testClassType)
+ private void InitializeCustomCodeOptions(AppiumOptions options, Type testClassType)
{
var customCodeOptions = ServicesCollection.Current.Resolve>($"caps-{testClassType.FullName}");
if (customCodeOptions != null && customCodeOptions.Count > 0)
@@ -301,13 +301,29 @@ private void InitializeCustomCodeOptions(dynamic options, Type testClassType)
{
if (!string.IsNullOrEmpty(item.Key) && !string.IsNullOrEmpty(item.Value))
{
- options.AddAdditionalAppiumOption(item.Key, FormatGridOptions(item.Value, testClassType));
+ switch (item.Key) {
+ case "app":
+ options.App = (string)FormatGridOptions(item.Value, testClassType);
+ break;
+ case "deviceName":
+ options.DeviceName = (string)FormatGridOptions(item.Value, testClassType);
+ break;
+ case "automationName":
+ options.AutomationName = (string)FormatGridOptions(item.Value, testClassType);
+ break;
+ case "platformVersion":
+ options.PlatformVersion = (string)FormatGridOptions(item.Value, testClassType);
+ break;
+ default:
+ options.AddAdditionalAppiumOption(item.Key, FormatGridOptions(item.Value, testClassType));
+ break;
+ }
}
}
}
}
- private void InitializeGridOptionsFromConfiguration(dynamic options, Type testClassType)
+ private void InitializeGridOptionsFromConfiguration(AppiumOptions options, Type testClassType)
{
if (ConfigurationService.GetSection().ExecutionSettings.Arguments == null)
{
@@ -320,13 +336,29 @@ private void InitializeGridOptionsFromConfiguration(dynamic options, Type testCl
{
if (!string.IsNullOrEmpty(item.Key) && !string.IsNullOrEmpty(item.Value))
{
- options.AddAdditionalAppiumOption(item.Key, FormatGridOptions(item.Value, testClassType));
+ switch (item.Key) {
+ case "app":
+ options.App = (string)FormatGridOptions(item.Value, testClassType);
+ break;
+ case "deviceName":
+ options.DeviceName = (string)FormatGridOptions(item.Value, testClassType);
+ break;
+ case "automationName":
+ options.AutomationName = (string)FormatGridOptions(item.Value, testClassType);
+ break;
+ case "platformVersion":
+ options.PlatformVersion = (string)FormatGridOptions(item.Value, testClassType);
+ break;
+ default:
+ options.AddAdditionalAppiumOption(item.Key, FormatGridOptions(item.Value, testClassType));
+ break;
+ }
}
}
}
}
- private dynamic FormatGridOptions(string option, Type testClassType)
+ private object FormatGridOptions(string option, Type testClassType)
{
if (bool.TryParse(option, out bool result))
{
@@ -355,4 +387,4 @@ private dynamic FormatGridOptions(string option, Type testClassType)
return option.Replace("{runName}", timestamp).Replace("{runName}", runName);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Bellatrix.Mobile/services/Android/AndroidAppService.cs b/src/Bellatrix.Mobile/services/Android/AndroidAppService.cs
index eec43f379..471a0a44c 100644
--- a/src/Bellatrix.Mobile/services/Android/AndroidAppService.cs
+++ b/src/Bellatrix.Mobile/services/Android/AndroidAppService.cs
@@ -11,7 +11,10 @@
//
// Anton Angelov
// https://bellatrix.solutions/
+using DocumentFormat.OpenXml.Bibliography;
+using OpenQA.Selenium;
using OpenQA.Selenium.Appium.Android;
+using System.Collections.Generic;
namespace Bellatrix.Mobile.Services.Android;
@@ -40,10 +43,28 @@ public void StartActivity(
// ignore
}
- WrappedAppiumDriver.StartActivity(appPackage, appActivity, appWaitPackage, appWaitActivity, stopApp);
+ var args = new Dictionary
+ {
+ ["appPackage"] = appPackage,
+ ["appActivity"] = appActivity,
+ ["appWaitPackage"] = appWaitPackage,
+ ["appWaitActivity"] = appWaitActivity,
+ ["dontStopAppOnReset"] = !stopApp
+ };
+
+ ((IJavaScriptExecutor)WrappedAppiumDriver).ExecuteScript("mobile: startActivity", args);
}
- public void StartActivityWithIntent(string appPackage, string appActivity, string intentAction, string appWaitPackage = "", string appWaitActivity = "", string intentCategory = "", string intentFlags = "", string intentOptionalArgs = "", bool stopApp = true)
+ public void StartActivityWithIntent(
+ string appPackage,
+ string appActivity,
+ string intentAction,
+ string appWaitPackage = "",
+ string appWaitActivity = "",
+ string intentCategory = "",
+ string intentFlags = "",
+ string intentOptionalArgs = "",
+ bool stopApp = true)
{
try
{
@@ -54,6 +75,43 @@ public void StartActivityWithIntent(string appPackage, string appActivity, strin
// ignore
}
- WrappedAppiumDriver.StartActivityWithIntent(appPackage, appActivity, intentAction, appWaitPackage, appWaitActivity, intentCategory, intentFlags, intentOptionalArgs, stopApp);
+ // Build optional intent parameters
+ var optionalIntentArguments = new List();
+
+ if (!string.IsNullOrWhiteSpace(intentAction))
+ optionalIntentArguments.Add($"-a {intentAction}");
+
+ if (!string.IsNullOrWhiteSpace(intentCategory))
+ optionalIntentArguments.Add($"-c {intentCategory}");
+
+ // flags can be like: "0x10200000" or already "-f 0x10200000"
+ if (!string.IsNullOrWhiteSpace(intentFlags))
+ {
+ optionalIntentArguments.Add(intentFlags.TrimStart().StartsWith("-f ")
+ ? intentFlags
+ : $"-f {intentFlags}");
+ }
+
+ // anything extra the caller passes, e.g. "-d https://..." or "--es key value"
+ if (!string.IsNullOrWhiteSpace(intentOptionalArgs))
+ optionalIntentArguments.Add(intentOptionalArgs);
+
+ var args = new Dictionary
+ {
+ ["appPackage"] = appPackage,
+ ["appActivity"] = appActivity,
+
+ // These two are optional, but very useful when the launch triggers redirects/splash
+ ["appWaitPackage"] = string.IsNullOrWhiteSpace(appWaitPackage) ? appPackage : appWaitPackage,
+ ["appWaitActivity"] = string.IsNullOrWhiteSpace(appWaitActivity) ? appActivity : appWaitActivity,
+
+ // Appium 2+ accepts intent args here for UiAutomator2
+ ["intentArguments"] = optionalIntentArguments,
+
+ // stopApp behavior mapping (depends on your old semantics)
+ ["dontStopAppOnReset"] = !stopApp
+ };
+
+ ((IJavaScriptExecutor)WrappedAppiumDriver).ExecuteScript("mobile: startActivity", args);
}
}
\ No newline at end of file
diff --git a/src/Bellatrix.Mobile/services/Android/AndroidDeviceService.cs b/src/Bellatrix.Mobile/services/Android/AndroidDeviceService.cs
index f3abd7af1..2a9631e07 100644
--- a/src/Bellatrix.Mobile/services/Android/AndroidDeviceService.cs
+++ b/src/Bellatrix.Mobile/services/Android/AndroidDeviceService.cs
@@ -46,16 +46,65 @@ public bool IsLocked
public ConnectionType ConnectionType
{
- get => WrappedAppiumDriver.ConnectionType;
+ get
+ {
+ // airplane_mode_on: "1" = ON, "0" = OFF
+ var airplane = ShellGet("settings", "get global airplane_mode_on").Trim();
+ if (airplane == "1") return ConnectionType.AirplaneMode;
+
+ // These return "enabled"/"disabled" on most devices
+ var wifi = ShellGet("svc", "wifi").Trim().ToLowerInvariant();
+ var data = ShellGet("svc", "data").Trim().ToLowerInvariant();
+
+ bool wifiOn = wifi.Contains("enabled");
+ bool dataOn = data.Contains("enabled");
+
+ if (wifiOn && dataOn) return ConnectionType.AllNetworkOn;
+ if (wifiOn) return ConnectionType.WifiOnly;
+ if (dataOn) return ConnectionType.DataOnly;
+
+ return ConnectionType.None;
+ }
set
{
- try
- {
- WrappedAppiumDriver.ConnectionType = value;
- }
- catch (WebDriverException ex) when (ex.Message.Contains("An unknown server-side error occurred while processing the command"))
+ try { WrappedAppiumDriver.HideKeyboard(); } catch { /* ignore */ }
+
+ switch (value)
{
- throw new AppiumEngineException(ex);
+ case ConnectionType.AirplaneMode:
+ // Turn airplane mode on; (broadcast helps some OEMs apply it)
+ Shell("settings", "put global airplane_mode_on 1");
+ Shell("am", "broadcast -a android.intent.action.AIRPLANE_MODE --ez state true");
+ // Typically disables radios, but OEMs vary
+ break;
+
+ case ConnectionType.None:
+ Shell("settings", "put global airplane_mode_on 0");
+ Shell("am", "broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
+ Shell("svc", "wifi disable");
+ Shell("svc", "data disable");
+ break;
+
+ case ConnectionType.WifiOnly:
+ Shell("settings", "put global airplane_mode_on 0");
+ Shell("am", "broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
+ Shell("svc", "wifi enable");
+ Shell("svc", "data disable");
+ break;
+
+ case ConnectionType.DataOnly:
+ Shell("settings", "put global airplane_mode_on 0");
+ Shell("am", "broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
+ Shell("svc", "wifi disable");
+ Shell("svc", "data enable");
+ break;
+
+ case ConnectionType.AllNetworkOn:
+ Shell("settings", "put global airplane_mode_on 0");
+ Shell("am", "broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
+ Shell("svc", "wifi enable");
+ Shell("svc", "data enable");
+ break;
}
}
}
@@ -66,4 +115,36 @@ public ConnectionType ConnectionType
public void TurnOnLocationService() => WrappedAppiumDriver.ToggleLocationServices();
public void OpenNotifications() => WrappedAppiumDriver.OpenNotifications();
public void SetSetting(string setting, object value) => WrappedAppiumDriver.SetSetting(setting, value);
+
+ private void Shell(string command, string args)
+ {
+ var p = new Dictionary
+ {
+ ["command"] = command,
+ ["args"] = args.Split(' ', StringSplitOptions.RemoveEmptyEntries),
+ ["includeStderr"] = true,
+ ["timeout"] = 20000
+ };
+
+ ((IJavaScriptExecutor)WrappedAppiumDriver).ExecuteScript("mobile: shell", p);
+ }
+
+ private string ShellGet(string command, string args)
+ {
+ var p = new Dictionary
+ {
+ ["command"] = command,
+ ["args"] = args.Split(' ', StringSplitOptions.RemoveEmptyEntries),
+ ["includeStderr"] = true,
+ ["timeout"] = 20000
+ };
+
+ var result = ((IJavaScriptExecutor)WrappedAppiumDriver).ExecuteScript("mobile: shell", p);
+
+ // Appium returns a dictionary with "stdout"/"stderr" on many setups
+ if (result is IDictionary dict && dict.TryGetValue("stdout", out var stdout))
+ return stdout?.ToString() ?? string.Empty;
+
+ return result?.ToString() ?? string.Empty;
+ }
}
diff --git a/src/Bellatrix.Playwright.Tests/Bellatrix.Playwright.Tests.csproj b/src/Bellatrix.Playwright.Tests/Bellatrix.Playwright.Tests.csproj
index 954c3333a..2a182876f 100644
--- a/src/Bellatrix.Playwright.Tests/Bellatrix.Playwright.Tests.csproj
+++ b/src/Bellatrix.Playwright.Tests/Bellatrix.Playwright.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net10.0
enable
enable
@@ -28,10 +28,10 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
-
-
+
+
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsChrome.cs b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsChrome.cs
index c6fc25dd3..e0da4da3d 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsChrome.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsChrome.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -77,7 +77,7 @@ public void ReturnButtonHtml_When_InnerHtml_Chrome()
{
var anchorElement = App.Components.CreateById("myAnchor4");
- Assert.IsTrue(anchorElement.InnerHtml.Contains("Click me"));
+ Assert.Contains("Click me", anchorElement.InnerHtml);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsChromeHeadless.cs b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsChromeHeadless.cs
index 03d34174c..0b1e06c39 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsChromeHeadless.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsChromeHeadless.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ public void ReturnButtonHtml_When_InnerHtml_ChromeHeadless()
{
var anchorElement = App.Components.CreateById("myAnchor4");
- Assert.IsTrue(anchorElement.InnerHtml.Contains("Click me"));
+ Assert.Contains("Click me", anchorElement.InnerHtml);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsEdge.cs b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsEdge.cs
index 3839f5674..546629921 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsEdge.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsEdge.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -83,7 +83,7 @@ public void ReturnButtonHtml_When_InnerHtml_Edge()
{
var anchorElement = App.Components.CreateById("myAnchor4");
- Assert.IsTrue(anchorElement.InnerHtml.Contains("Click me"));
+ Assert.Contains("Click me", anchorElement.InnerHtml);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsFirefox.cs b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsFirefox.cs
index 4833e89a1..02efec2cf 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsFirefox.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsFirefox.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -75,7 +75,7 @@ public void ReturnButtonHtml_When_InnerHtml_Firefox()
{
var anchorElement = App.Components.CreateById("myAnchor4");
- Assert.IsTrue(anchorElement.InnerHtml.Contains("Click me"));
+ Assert.Contains("Click me", anchorElement.InnerHtml);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsFirefoxHeadless.cs b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsFirefoxHeadless.cs
index ca60775ad..8620d3740 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsFirefoxHeadless.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsFirefoxHeadless.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ public void ReturnButtonHtml_When_InnerHtml_FirefoxHeadless()
{
var anchorElement = App.Components.CreateById("myAnchor4");
- Assert.IsTrue(anchorElement.InnerHtml.Contains("Click me"));
+ Assert.Contains("Click me", anchorElement.InnerHtml);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsSafari.cs b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsSafari.cs
index 77d3f76ba..5f850c800 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsSafari.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlTestsSafari.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -71,7 +71,7 @@ public void ReturnButtonHtml_When_InnerHtml_Safari()
{
var anchorElement = App.Components.CreateById("myAnchor4");
- Assert.IsTrue(anchorElement.InnerHtml.Contains("Click me"));
+ Assert.Contains("Click me", anchorElement.InnerHtml);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlValidateExtensionsExceptionMessagesTests.cs b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlValidateExtensionsExceptionMessagesTests.cs
index c0772765d..d6430a844 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlValidateExtensionsExceptionMessagesTests.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Anchor/AnchorControlValidateExtensionsExceptionMessagesTests.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ public void CorrectExceptionMessageSet_When_ValidateStyleIsThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's style should be 'color: blue;' but was 'color: red;'. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -63,7 +63,7 @@ public void CorrectExceptionMessageSet_When_ValidateStyleContainsThrowsException
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's style should contains 'color: blue;' but it is 'color: red;'. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -82,7 +82,7 @@ public void CorrectExceptionMessageSet_When_ValidateStyleNotContainsThrowsExcept
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's style should not contains 'color: red;' but it was 'color: red;'. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -99,7 +99,7 @@ public void CorrectExceptionMessageSet_When_ValidateInnerTextIsThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's inner text should be 'Bellatrix' but was 'Automate The Planet'. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -116,7 +116,7 @@ public void CorrectExceptionMessageSet_When_ValidateInnerHtmlIsIsThrowsException
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's inner HTML should be '
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ public void CorrectExceptionMessageSet_When_ValidateIsNotDisabledThrowsException
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control should NOT be disabled but it was. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -59,7 +59,7 @@ public void CorrectExceptionMessageSet_When_ValidateIsDisabledThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control should be disabled but it was NOT. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -76,7 +76,7 @@ public void CorrectExceptionMessageSet_When_ValidateValueIsThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's value should be 'Start' but was 'Stop'. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -93,7 +93,7 @@ public void CorrectExceptionMessageSet_When_ValidateValueIsNullThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's value should be null but was 'Start'. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
}
\ No newline at end of file
diff --git a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsChrome.cs b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsChrome.cs
index e33f5f803..8ad87d509 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsChrome.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsChrome.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ public void Unchecked_When_UseCheckMethod_Chrome()
checkBoxElement.Check();
- Assert.AreEqual(false, checkBoxElement.IsChecked);
+ Assert.IsFalse(checkBoxElement.IsChecked);
}
[TestMethod]
@@ -41,7 +41,7 @@ public void Unchecked_When_UseUncheckMethod_Chrome()
checkBoxElement.Uncheck();
- Assert.AreEqual(false, checkBoxElement.IsChecked);
+ Assert.IsFalse(checkBoxElement.IsChecked);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsEdge.cs b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsEdge.cs
index da1557c11..18d8d218b 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsEdge.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsEdge.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@ public void Unchecked_When_UseCheckMethod_Edge()
checkBoxElement.Check();
- Assert.AreEqual(false, checkBoxElement.IsChecked);
+ Assert.IsFalse(checkBoxElement.IsChecked);
}
[TestMethod]
@@ -44,7 +44,7 @@ public void Unchecked_When_UseUncheckMethod_Edge()
checkBoxElement.Uncheck();
- Assert.AreEqual(false, checkBoxElement.IsChecked);
+ Assert.IsFalse(checkBoxElement.IsChecked);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsFirefox.cs b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsFirefox.cs
index df6eff793..ca0833903 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsFirefox.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsFirefox.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ public void Unchecked_When_UseCheckMethod_Firefox()
checkBoxElement.Check();
- Assert.AreEqual(false, checkBoxElement.IsChecked);
+ Assert.IsFalse(checkBoxElement.IsChecked);
}
[TestMethod]
@@ -41,7 +41,7 @@ public void Unchecked_When_UseUncheckMethod_Firefox()
checkBoxElement.Uncheck();
- Assert.AreEqual(false, checkBoxElement.IsChecked);
+ Assert.IsFalse(checkBoxElement.IsChecked);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsSafari.cs b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsSafari.cs
index 279f20687..15c044373 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsSafari.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlTestsSafari.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ public void Unchecked_When_UseCheckMethod_Safari()
checkBoxElement.Check();
- Assert.AreEqual(false, checkBoxElement.IsChecked);
+ Assert.IsFalse(checkBoxElement.IsChecked);
}
[TestMethod]
@@ -41,7 +41,7 @@ public void Unchecked_When_UseUncheckMethod_Safari()
checkBoxElement.Uncheck();
- Assert.AreEqual(false, checkBoxElement.IsChecked);
+ Assert.IsFalse(checkBoxElement.IsChecked);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlValidateExtensionsExceptionMessagesTests.cs b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlValidateExtensionsExceptionMessagesTests.cs
index 0c78703b8..8972607b8 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlValidateExtensionsExceptionMessagesTests.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/CheckBox/CheckBoxControlValidateExtensionsExceptionMessagesTests.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ public void CorrectExceptionMessageSet_When_ValidateCheckedThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control should be checked but was NOT. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -62,7 +62,7 @@ public void CorrectExceptionMessageSet_When_ValidateNotCheckedThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control should be not checked but it WAS. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
}
\ No newline at end of file
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsChrome.cs b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsChrome.cs
index 6693581b5..a1cd7a212 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsChrome.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsChrome.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -77,7 +77,7 @@ public void GetRequiredReturnsFalse_When_RequiredAttributeIsNotPresent_Chrome()
{
var colorElement = App.Components.CreateById("myColor4");
- Assert.AreEqual(false, colorElement.IsRequired);
+ Assert.IsFalse(colorElement.IsRequired);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsEdge.cs b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsEdge.cs
index ac99a0861..3ec6ea57c 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsEdge.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsEdge.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -84,7 +84,7 @@ public void GetRequiredReturnsFalse_When_RequiredAttributeIsNotPresent_Edge()
{
var colorElement = App.Components.CreateById("myColor4");
- Assert.AreEqual(false, colorElement.IsRequired);
+ Assert.IsFalse(colorElement.IsRequired);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsFirefox.cs b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsFirefox.cs
index c86af57b5..857815281 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsFirefox.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsFirefox.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -77,7 +77,7 @@ public void GetRequiredReturnsFalse_When_RequiredAttributeIsNotPresent_Firefox()
{
var colorElement = App.Components.CreateById("myColor4");
- Assert.AreEqual(false, colorElement.IsRequired);
+ Assert.IsFalse(colorElement.IsRequired);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsSafari.cs b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsSafari.cs
index c1e5d1136..b79daa4cd 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsSafari.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlTestsSafari.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -76,7 +76,7 @@ public void GetRequiredReturnsFalse_When_RequiredAttributeIsNotPresent_Safari()
{
var colorElement = App.Components.CreateById("myColor4");
- Assert.AreEqual(false, colorElement.IsRequired);
+ Assert.IsFalse(colorElement.IsRequired);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlValidateExtensionsExceptionMessagesTests.cs b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlValidateExtensionsExceptionMessagesTests.cs
index 4bd9bd109..9d99a389f 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlValidateExtensionsExceptionMessagesTests.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Color/ColorControlValidateExtensionsExceptionMessagesTests.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -44,7 +44,7 @@ public void CorrectExceptionMessageSet_When_ValidateColorIsThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's color should be '#f00031' but was '#f00030'. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -61,7 +61,7 @@ public void CorrectExceptionMessageSet_When_ValidateAutoCompleteOffThrowsExcepti
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control autocomplete should be OFF but was not. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -78,7 +78,7 @@ public void CorrectExceptionMessageSet_When_ValidateAutoCompleteOnThrowsExceptio
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control autocomplete should be ON but was not. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -95,7 +95,7 @@ public void CorrectExceptionMessageSet_When_ValidateNotRequiredThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control should be NOT required but was NOT. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -112,7 +112,7 @@ public void CorrectExceptionMessageSet_When_ValidateRequiredThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control should be required but was NOT. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -129,7 +129,7 @@ public void CorrectExceptionMessageSet_When_ValidateListIsNullThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's list should be null but was 'tickmarks'. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
@@ -146,7 +146,7 @@ public void CorrectExceptionMessageSet_When_ValidateListIsThrowsException()
catch (ComponentPropertyValidateException e)
{
string expectedExceptionMessage = $"The control's list should be 'tickmarks' but was ''. The test failed on URL:";
- Assert.AreEqual(true, e.Message.Contains(expectedExceptionMessage), $"Should be {expectedExceptionMessage} but was {e.Message}");
+ Assert.Contains(expectedExceptionMessage, e.Message, $"Should be {expectedExceptionMessage} but was {e.Message}");
}
}
}
\ No newline at end of file
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Date/DateControlTestsChrome.cs b/src/Bellatrix.Playwright.Tests/Controls/Date/DateControlTestsChrome.cs
index d2c66c623..5a62fa33e 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Date/DateControlTestsChrome.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Date/DateControlTestsChrome.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -108,7 +108,7 @@ public void GetReadonlyReturnsFalse_When_ReadonlyAttributeIsNotPresent_Chrome()
{
var dateElement = App.Components.CreateById("myDate4");
- Assert.AreEqual(false, dateElement.IsReadonly);
+ Assert.IsFalse(dateElement.IsReadonly);
}
[TestMethod]
@@ -117,7 +117,7 @@ public void GetReadonlyReturnsTrue_When_ReadonlyAttributeIsPresent_Chrome()
{
var dateElement = App.Components.CreateById("myDate5");
- Assert.AreEqual(true, dateElement.IsReadonly);
+ Assert.IsTrue(dateElement.IsReadonly);
}
[TestMethod]
@@ -182,7 +182,7 @@ public void GetRequiredReturnsFalse_When_RequiredAttributeIsNotPresent_Chrome()
{
var dateElement = App.Components.CreateById("myDate4");
- Assert.AreEqual(false, dateElement.IsRequired);
+ Assert.IsFalse(dateElement.IsRequired);
}
[TestMethod]
diff --git a/src/Bellatrix.Playwright.Tests/Controls/Date/DateControlTestsEdge.cs b/src/Bellatrix.Playwright.Tests/Controls/Date/DateControlTestsEdge.cs
index b1cee0a17..fe8d0cd38 100644
--- a/src/Bellatrix.Playwright.Tests/Controls/Date/DateControlTestsEdge.cs
+++ b/src/Bellatrix.Playwright.Tests/Controls/Date/DateControlTestsEdge.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright 2025 Automate The Planet Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// You may not use this file except in compliance with the License.
@@ -118,7 +118,7 @@ public void GetReadonlyReturnsFalse_When_ReadonlyAttributeIsNotPresent_Edge()
{
var dateElement = App.Components.CreateById