diff --git a/README.md b/README.md index ea1a81e1..731fc0eb 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,12 @@ Testware provides capabilities to automate: Chrome Firefox IE - Edge - + Edge +- **Websites** (using Selenoid) + - Supported Browsers: + Chrome + Firefox + Edge - **Mobile Applications** (using Appium) - **Windows Desktop applications** (using WinAppDriver) - **API Rest** (using Restsharp) @@ -155,6 +159,53 @@ Evidence collection: } ] }, + { + "Tag": "RemoteDriver", + "Capabilities": [ + { + "Name": "Chrome", + "Uri": "http://localhost:4444/wd/hub", + "BrowserName": "Chrome", + "BrowserVersion": "111.0", + "Resolution": "1920x1080x24", + "EnableLog": false, + "EnableVnc": true, + "EnableVideo": false, + "CommandTimeOutInMinutes": 5, + "Arguments": [ + "--start-maximized" + ] + }, + { + "Name": "Firefox", + "Uri": "http://localhost:4444/wd/hub", + "BrowserName": "Firefox", + "BrowserVersion": "110.0", + "Resolution": "1920x1080x24", + "EnableLog": false, + "EnableVnc": true, + "EnableVideo": false, + "CommandTimeOutInMinutes": 5, + "Arguments": [ + "--start-maximized" + ] + }, + { + "Name": "Edge", + "Uri": "http://localhost:4444/wd/hub", + "BrowserName": "Edge", + "BrowserVersion": "111.0", + "Resolution": "1920x1080x24", + "EnableLog": false, + "EnableVnc": true, + "EnableVideo": false, + "CommandTimeOutInMinutes": 5, + "Arguments": [ + "--start-maximized" + ] + } + ] + }, { "Name": "Appiumdriver", "AppiumUrl": "http://127.0.0.1:4723/wd/hub", diff --git a/TestWare.sln b/TestWare.sln index ed9de8ba..56ee7b42 100644 --- a/TestWare.sln +++ b/TestWare.sln @@ -38,11 +38,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWare.ExtentReport", "sr EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Reporting", "Reporting", "{DCCEF363-0EBE-46EA-B02B-CD59010626F6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWare.Engines.Selenoid", "src\Engines\TestWare.Engines.Selenoid\TestWare.Engines.Selenoid.csproj", "{0B7E3DFE-AB55-4C0C-81C6-6A6F517475EE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWare.Samples.Selenoid.Web", "samples\TestWare.Samples.Selenoid.Web\TestWare.Samples.Selenoid.Web.csproj", "{672C5D48-DD50-4AA3-8974-50B95746EA07}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWare.Engines.MongoDB", "src\Engines\TestWare.Engines.MongoDB\TestWare.Engines.MongoDB.csproj", "{4DFC2BE5-D3EF-4F39-AAD4-B8213DE92A11}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWare.Samples.MongoDB", "samples\TestWare.Samples.MongoDB\TestWare.Samples.MongoDB.csproj", "{5378DE68-675E-440D-AAA9-7D3AF8AA680E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWare.Samples.MongoDB", "samples\TestWare.Samples.MongoDB\TestWare.Samples.MongoDB.csproj", "{5378DE68-675E-440D-AAA9-7D3AF8AA680E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWare.AllureReport", "src\Core\TestWare.AllureReport\TestWare.AllureReport.csproj", "{91F2B723-A68B-4711-969B-89897B3C6161}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWare.AllureReport", "src\Core\TestWare.AllureReport\TestWare.AllureReport.csproj", "{91F2B723-A68B-4711-969B-89897B3C6161}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestWare.Engines.Common", "src\Engines\TestWare.Engines.Common\TestWare.Engines.Common\TestWare.Engines.Common.csproj", "{FFD6A200-D2F1-4CBE-8FDC-320C7839AAB9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -90,6 +96,14 @@ Global {94025C6B-DF0D-4AC4-BBC5-A94A9FA3AB0A}.Debug|Any CPU.Build.0 = Debug|Any CPU {94025C6B-DF0D-4AC4-BBC5-A94A9FA3AB0A}.Release|Any CPU.ActiveCfg = Release|Any CPU {94025C6B-DF0D-4AC4-BBC5-A94A9FA3AB0A}.Release|Any CPU.Build.0 = Release|Any CPU + {0B7E3DFE-AB55-4C0C-81C6-6A6F517475EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B7E3DFE-AB55-4C0C-81C6-6A6F517475EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B7E3DFE-AB55-4C0C-81C6-6A6F517475EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B7E3DFE-AB55-4C0C-81C6-6A6F517475EE}.Release|Any CPU.Build.0 = Release|Any CPU + {672C5D48-DD50-4AA3-8974-50B95746EA07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {672C5D48-DD50-4AA3-8974-50B95746EA07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {672C5D48-DD50-4AA3-8974-50B95746EA07}.Release|Any CPU.ActiveCfg = Release|Any CPU + {672C5D48-DD50-4AA3-8974-50B95746EA07}.Release|Any CPU.Build.0 = Release|Any CPU {4DFC2BE5-D3EF-4F39-AAD4-B8213DE92A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4DFC2BE5-D3EF-4F39-AAD4-B8213DE92A11}.Debug|Any CPU.Build.0 = Debug|Any CPU {4DFC2BE5-D3EF-4F39-AAD4-B8213DE92A11}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -102,6 +116,10 @@ Global {91F2B723-A68B-4711-969B-89897B3C6161}.Debug|Any CPU.Build.0 = Debug|Any CPU {91F2B723-A68B-4711-969B-89897B3C6161}.Release|Any CPU.ActiveCfg = Release|Any CPU {91F2B723-A68B-4711-969B-89897B3C6161}.Release|Any CPU.Build.0 = Release|Any CPU + {FFD6A200-D2F1-4CBE-8FDC-320C7839AAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FFD6A200-D2F1-4CBE-8FDC-320C7839AAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FFD6A200-D2F1-4CBE-8FDC-320C7839AAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FFD6A200-D2F1-4CBE-8FDC-320C7839AAB9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -120,9 +138,12 @@ Global {71120454-DD7A-484C-93F2-699B4C363297} = {58B1446D-98A3-46A2-A668-305F0170B39F} {94025C6B-DF0D-4AC4-BBC5-A94A9FA3AB0A} = {DCCEF363-0EBE-46EA-B02B-CD59010626F6} {DCCEF363-0EBE-46EA-B02B-CD59010626F6} = {4678C707-68DB-4E06-9184-A07FB398832C} + {0B7E3DFE-AB55-4C0C-81C6-6A6F517475EE} = {A2E4E1F7-CB61-48DC-AA38-A74F7DC74CA2} + {672C5D48-DD50-4AA3-8974-50B95746EA07} = {58B1446D-98A3-46A2-A668-305F0170B39F} {4DFC2BE5-D3EF-4F39-AAD4-B8213DE92A11} = {A2E4E1F7-CB61-48DC-AA38-A74F7DC74CA2} {5378DE68-675E-440D-AAA9-7D3AF8AA680E} = {58B1446D-98A3-46A2-A668-305F0170B39F} {91F2B723-A68B-4711-969B-89897B3C6161} = {DCCEF363-0EBE-46EA-B02B-CD59010626F6} + {FFD6A200-D2F1-4CBE-8FDC-320C7839AAB9} = {A2E4E1F7-CB61-48DC-AA38-A74F7DC74CA2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4F88CB24-23C8-4E0E-8B83-29023C1642B8} diff --git a/samples/TestWare.Samples.API/TestWare.Samples.API.csproj b/samples/TestWare.Samples.API/TestWare.Samples.API.csproj index 73379e5a..3dd1aa9f 100644 --- a/samples/TestWare.Samples.API/TestWare.Samples.API.csproj +++ b/samples/TestWare.Samples.API/TestWare.Samples.API.csproj @@ -6,13 +6,13 @@ + - - + - + diff --git a/samples/TestWare.Samples.Appium.Mobile/POM/Cart/CartPage.cs b/samples/TestWare.Samples.Appium.Mobile/POM/Cart/CartPage.cs index 117292ee..1e9bd672 100644 --- a/samples/TestWare.Samples.Appium.Mobile/POM/Cart/CartPage.cs +++ b/samples/TestWare.Samples.Appium.Mobile/POM/Cart/CartPage.cs @@ -1,6 +1,6 @@ using OpenQA.Selenium; using TestWare.Engines.Appium.Pages; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.Factory; using TestWare.Core.Libraries; diff --git a/samples/TestWare.Samples.Appium.Mobile/POM/Login/LoginPage.cs b/samples/TestWare.Samples.Appium.Mobile/POM/Login/LoginPage.cs index 1ec030bb..d07a842d 100644 --- a/samples/TestWare.Samples.Appium.Mobile/POM/Login/LoginPage.cs +++ b/samples/TestWare.Samples.Appium.Mobile/POM/Login/LoginPage.cs @@ -1,5 +1,5 @@ using OpenQA.Selenium; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.Factory; using TestWare.Engines.Appium.Pages; diff --git a/samples/TestWare.Samples.Appium.Mobile/POM/Menu/MenuPage.cs b/samples/TestWare.Samples.Appium.Mobile/POM/Menu/MenuPage.cs index 3a5a060b..dd3d687b 100644 --- a/samples/TestWare.Samples.Appium.Mobile/POM/Menu/MenuPage.cs +++ b/samples/TestWare.Samples.Appium.Mobile/POM/Menu/MenuPage.cs @@ -1,6 +1,6 @@ using OpenQA.Selenium; using TestWare.Engines.Appium.Pages; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.Factory; namespace TestWare.Samples.Appium.Mobile.POM.Menu; diff --git a/samples/TestWare.Samples.Appium.Mobile/POM/Product/ProductPage.cs b/samples/TestWare.Samples.Appium.Mobile/POM/Product/ProductPage.cs index 8e018e54..a81fdfc2 100644 --- a/samples/TestWare.Samples.Appium.Mobile/POM/Product/ProductPage.cs +++ b/samples/TestWare.Samples.Appium.Mobile/POM/Product/ProductPage.cs @@ -1,7 +1,7 @@ using OpenQA.Selenium; using OpenQA.Selenium.Appium; using TestWare.Core.Libraries; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.Factory; using TestWare.Engines.Appium.Pages; diff --git a/samples/TestWare.Samples.Appium.Mobile/TestWare.Samples.Appium.Mobile.csproj b/samples/TestWare.Samples.Appium.Mobile/TestWare.Samples.Appium.Mobile.csproj index 59c4be80..12be5a98 100644 --- a/samples/TestWare.Samples.Appium.Mobile/TestWare.Samples.Appium.Mobile.csproj +++ b/samples/TestWare.Samples.Appium.Mobile/TestWare.Samples.Appium.Mobile.csproj @@ -7,12 +7,12 @@ + - - + - + diff --git a/samples/TestWare.Samples.MongoDB/TestWare.Samples.MongoDB.csproj b/samples/TestWare.Samples.MongoDB/TestWare.Samples.MongoDB.csproj index 5f7c44b4..4319e2de 100644 --- a/samples/TestWare.Samples.MongoDB/TestWare.Samples.MongoDB.csproj +++ b/samples/TestWare.Samples.MongoDB/TestWare.Samples.MongoDB.csproj @@ -9,7 +9,7 @@ - + diff --git a/samples/TestWare.Samples.Selenium.Web/POM/Stinto/Chat/ChatPage.cs b/samples/TestWare.Samples.Selenium.Web/POM/Stinto/Chat/ChatPage.cs index cfed90a4..a2646dbf 100644 --- a/samples/TestWare.Samples.Selenium.Web/POM/Stinto/Chat/ChatPage.cs +++ b/samples/TestWare.Samples.Selenium.Web/POM/Stinto/Chat/ChatPage.cs @@ -1,6 +1,6 @@ using OpenQA.Selenium; using TestWare.Core.Libraries; -using TestWare.Engines.Selenium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Selenium.Factory; using TestWare.Engines.Selenium.Pages; diff --git a/samples/TestWare.Samples.Selenium.Web/POM/Stinto/CreateChat/CreateChatPage.cs b/samples/TestWare.Samples.Selenium.Web/POM/Stinto/CreateChat/CreateChatPage.cs index 2b788a26..8f9c02c1 100644 --- a/samples/TestWare.Samples.Selenium.Web/POM/Stinto/CreateChat/CreateChatPage.cs +++ b/samples/TestWare.Samples.Selenium.Web/POM/Stinto/CreateChat/CreateChatPage.cs @@ -1,5 +1,5 @@ using OpenQA.Selenium; -using TestWare.Engines.Selenium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Selenium.Factory; using TestWare.Engines.Selenium.Pages; diff --git a/samples/TestWare.Samples.Selenium.Web/POM/Stinto/Home/HomePage.cs b/samples/TestWare.Samples.Selenium.Web/POM/Stinto/Home/HomePage.cs index 9fb3e451..38ddb933 100644 --- a/samples/TestWare.Samples.Selenium.Web/POM/Stinto/Home/HomePage.cs +++ b/samples/TestWare.Samples.Selenium.Web/POM/Stinto/Home/HomePage.cs @@ -1,5 +1,5 @@ using OpenQA.Selenium; -using TestWare.Engines.Selenium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Selenium.Factory; using TestWare.Engines.Selenium.Pages; diff --git a/samples/TestWare.Samples.Selenium.Web/POM/SwagLabs/Login/LoginPage.cs b/samples/TestWare.Samples.Selenium.Web/POM/SwagLabs/Login/LoginPage.cs index 82547164..8b1b1dc6 100644 --- a/samples/TestWare.Samples.Selenium.Web/POM/SwagLabs/Login/LoginPage.cs +++ b/samples/TestWare.Samples.Selenium.Web/POM/SwagLabs/Login/LoginPage.cs @@ -1,6 +1,6 @@ using OpenQA.Selenium; using TestWare.Core.Libraries; -using TestWare.Engines.Selenium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Selenium.Factory; using TestWare.Engines.Selenium.Pages; diff --git a/samples/TestWare.Samples.Selenium.Web/POM/SwagLabs/Menu/MenuPage.cs b/samples/TestWare.Samples.Selenium.Web/POM/SwagLabs/Menu/MenuPage.cs index 715dab7d..dc246ee6 100644 --- a/samples/TestWare.Samples.Selenium.Web/POM/SwagLabs/Menu/MenuPage.cs +++ b/samples/TestWare.Samples.Selenium.Web/POM/SwagLabs/Menu/MenuPage.cs @@ -1,5 +1,5 @@ using OpenQA.Selenium; -using TestWare.Engines.Selenium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Selenium.Factory; using TestWare.Engines.Selenium.Pages; diff --git a/samples/TestWare.Samples.Selenium.Web/TestWare.Samples.Selenium.Web.csproj b/samples/TestWare.Samples.Selenium.Web/TestWare.Samples.Selenium.Web.csproj index de40c02e..43cf4bdb 100644 --- a/samples/TestWare.Samples.Selenium.Web/TestWare.Samples.Selenium.Web.csproj +++ b/samples/TestWare.Samples.Selenium.Web/TestWare.Samples.Selenium.Web.csproj @@ -19,13 +19,13 @@ + - - - + + diff --git a/samples/TestWare.Samples.Selenoid.Web/Features/SwagLabs/Login.feature b/samples/TestWare.Samples.Selenoid.Web/Features/SwagLabs/Login.feature new file mode 100644 index 00000000..4ac466ba --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/Features/SwagLabs/Login.feature @@ -0,0 +1,32 @@ +@RemoteDriver +Feature: Login + +Scenario Outline: Login + Given the user enters username '' + And the user enters password '' + When the user clicks submit + Then the user can login + + @Chrome + Examples: + | Example Description | username | password | + | standard | standard_user | secret_sauce | + | problem | problem_user | secret_sauce | + + @Firefox + Examples: + | Example Description | username | password | + | standard | standard_user | secret_sauce | + | problem | problem_user | secret_sauce | + + @Edge + Examples: + | Example Description | username | password | + | standard | standard_user | secret_sauce | + | problem | problem_user | secret_sauce | + +@Chrome +Scenario: Logout + Given user 'standard_user' is logged with 'secret_sauce' into SwagLabs + When the user clicks Logout button + Then the user is at Login page diff --git a/samples/TestWare.Samples.Selenoid.Web/Hook.cs b/samples/TestWare.Samples.Selenoid.Web/Hook.cs new file mode 100644 index 00000000..742d855d --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/Hook.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using TestWare.Reporting.ExtentReport; + +namespace TestWare.Samples.Selenoid.Web; + +[Binding] +public sealed class Hook +{ + private readonly TestContext _testContext; + private int _stepCounter; + private static readonly LifeCycle _lifeCycle = new(); + private static ExtentReport _testReport; + + public Hook(TestContext testContext) + { + _testContext = testContext; + } + + [BeforeFeature] + public static void BeforeFeature(FeatureContext featureContext) + { + var name = featureContext.FeatureInfo.Title; + var tags = featureContext.FeatureInfo.Tags; + + _lifeCycle.BeginTestSuite(name); + _testReport.CreateFeature(name, tags); + } + + [AfterFeature] + public static void AfterFeature(FeatureContext featureContext) + { + _lifeCycle.EndTestSuite(); + } + + [BeforeScenario] + public void BeforeScenario(FeatureContext featureContext, ScenarioContext scenarioContext) + { + var name = scenarioContext.ScenarioInfo.Arguments.Count > 0 + ? $"{DateTime.UtcNow.ToString("yyy-MM-dd HH-mm-ss", CultureInfo.InvariantCulture)} - {scenarioContext.ScenarioInfo.Title}" + : scenarioContext.ScenarioInfo.Title; + + var description = scenarioContext.ScenarioInfo.Description ?? ""; + var scenarioTags = scenarioContext.ScenarioInfo.Tags; + _testReport.CreateTestCase(name, description, scenarioTags); + + _testContext.WriteLine("----------------------------------------- \r\n"); + _testContext.WriteLine($"Feature: {featureContext.FeatureInfo.Title}"); + _testContext.WriteLine($" Scenario: {scenarioContext.ScenarioInfo.Title} \r\n"); + + _stepCounter = 1; + var tags = GetTags(featureContext, scenarioContext); + _lifeCycle.BeginTestCase(name, tags); + } + + [AfterScenario] + public void AfterScenario() + { + _testReport.SetTestcaseOutcome(_testContext.CurrentTestOutcome); + _lifeCycle.EndTestCase(); + } + + [BeforeTestRun] + public static void BeforeTestRun() + { + _lifeCycle.BeginTestExecution(); + _testReport = new ExtentReport(_lifeCycle.GetCurrentResultsDirectory()); + } + + [AfterTestRun] + public static void AfterTestRun() + { + _lifeCycle.EndTestExecution(); + _testReport.CreateTestReportFile(); + } + + [BeforeStep] + public void BeforeStep(ScenarioContext scenarioContext) + { + var name = scenarioContext.CurrentScenarioBlock.ToString(); + var description = scenarioContext.StepContext.StepInfo.Text; + _testReport.CreateStep(name, description); + + var stepId = $"{_stepCounter:00} {description}"; + _stepCounter++; + _lifeCycle.BeginTestStep(stepId); + } + + [AfterStep] + public void AfterStep(ScenarioContext scenarioContext) + { + _lifeCycle.EndTestStep(); + var evidencesPath = _lifeCycle.GetStepEvidences(); + + foreach (var evidence in evidencesPath) + { + _testReport.AddScreenshotToStep(evidence); + _testContext.AddResultFile(evidence); + } + } + + private static List GetTags(FeatureContext featureContext, ScenarioContext scenarioContext) + { + var tags = featureContext.FeatureInfo.Tags.ToList(); + tags.AddRange(scenarioContext.ScenarioInfo.Tags.ToList()); + return tags; + } +} diff --git a/samples/TestWare.Samples.Selenoid.Web/ImplicitUsings.cs b/samples/TestWare.Samples.Selenoid.Web/ImplicitUsings.cs new file mode 100644 index 00000000..26bc47f3 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/ImplicitUsings.cs @@ -0,0 +1,3 @@ +global using FluentAssertions; +global using Microsoft.VisualStudio.TestTools.UnitTesting; +global using TechTalk.SpecFlow; diff --git a/samples/TestWare.Samples.Selenoid.Web/LifeCycle.cs b/samples/TestWare.Samples.Selenoid.Web/LifeCycle.cs new file mode 100644 index 00000000..ed161129 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/LifeCycle.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using System.Reflection; +using TestWare.Core; +using TestWare.Core.Configuration; +using TestWare.Core.Interfaces; +using TestWare.Engines.Selenoid; + +namespace TestWare.Samples.Selenoid.Web; + +internal class LifeCycle : AutomationLifeCycleBase +{ + protected override IEnumerable GetTestWareComponentAssemblies() + { + IEnumerable assemblies = new[] + { + typeof(Hook).Assembly + }; + + return assemblies; + } + + protected override IEnumerable GetTestWareEngines() + { + IEnumerable engines = new[] + { + new SelenoidManager() + }; + + return engines; + } + + protected override TestConfiguration GetConfiguration() + { + return ConfigurationManager.ReadConfigurationFile("TestConfiguration.Web.json"); + } +} diff --git a/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Inventory/IInventoryPage.cs b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Inventory/IInventoryPage.cs new file mode 100644 index 00000000..7cfcdb44 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Inventory/IInventoryPage.cs @@ -0,0 +1,9 @@ +using TestWare.Core.Interfaces; + +namespace TestWare.Samples.Selenoid.Web.POM.Inventory; + +public interface IInventoryPage : ITestWareComponent +{ + void CheckUserIsAtInventory(); +} + diff --git a/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Inventory/InventoryPage.cs b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Inventory/InventoryPage.cs new file mode 100644 index 00000000..54260bd1 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Inventory/InventoryPage.cs @@ -0,0 +1,27 @@ +using OpenQA.Selenium; +using TestWare.Core.Libraries; +using TestWare.Engines.Selenoid.Factory; +using TestWare.Engines.Selenoid.Pages; + +namespace TestWare.Samples.Selenoid.Web.POM.Inventory; + +public class InventoryPage : WebPage, IInventoryPage +{ + private const string InventoryUrl = "https://www.saucedemo.com/inventory.html"; + + public InventoryPage(IWebDriver driver) : base(driver) + { + } + + /// + public void CheckUserIsAtInventory() + { + RetryPolicies.ExecuteActionWithRetries( + () => + { + this.Driver.Url.Should().Be(InventoryUrl); + }); + } + +} + diff --git a/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Login/ILoginPage.cs b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Login/ILoginPage.cs new file mode 100644 index 00000000..49d6c09c --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Login/ILoginPage.cs @@ -0,0 +1,31 @@ +using TestWare.Core.Interfaces; + +namespace TestWare.Samples.Selenoid.Web.POM.Login; + +/// +/// Encapsulate all Loging busines logic +/// +public interface ILoginPage : ITestWareComponent +{ + /// + /// Script to send User Name + /// + /// + void EnterUserName(string name); + + /// + /// Script to send User Password + /// + /// + void EnterUserPassword(string password); + + /// + /// Click in Login button + /// + void ClickLoginButton(); + + /// + /// Check that the user is at Login Page + /// + void CheckUserIsAtLoginpage(); +} diff --git a/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Login/LoginPage.cs b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Login/LoginPage.cs new file mode 100644 index 00000000..e5eceb6d --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Login/LoginPage.cs @@ -0,0 +1,54 @@ +using OpenQA.Selenium; +using TestWare.Core.Libraries; +using TestWare.Engines.Common.Extras; +using TestWare.Engines.Selenoid.Factory; +using TestWare.Engines.Selenoid.Pages; + +namespace TestWare.Samples.Selenoid.Web.POM.Login; + +public class LoginPage : WebPage, ILoginPage +{ + private const string LoginUrl = "https://www.saucedemo.com/"; + + [FindsBy(How = How.Name, Using = "user-name")] + private IWebElement UserIdTextBox { get; set; } + + [FindsBy(How = How.Name, Using = "password")] + private IWebElement UserPasswordTextBox { get; set; } + + [FindsBy(How = How.Name, Using = "login-button")] + private IWebElement LoginButton { get; set; } + + public LoginPage(IWebDriver driver) : base(driver) + { + Url = LoginUrl; + NavigateToUrl(); + } + + /// + public void EnterUserName(string name) + => SendKeysElement(UserIdTextBox, name); + + /// + public void ConfirmLogoutPopup() + => this.Driver.SwitchTo().Alert().Accept(); + + /// + public void EnterUserPassword(string password) + => SendKeysElement(UserPasswordTextBox, password); + + /// + + public void ClickLoginButton() + => ClickElement(LoginButton); + + /// + public void CheckUserIsAtLoginpage() + { + RetryPolicies.ExecuteActionWithRetries( + () => + { + this.Driver.Url.Should().Be(LoginUrl); + }); + } +} diff --git a/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Menu/IMenuPage.cs b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Menu/IMenuPage.cs new file mode 100644 index 00000000..6d145167 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Menu/IMenuPage.cs @@ -0,0 +1,13 @@ +using TestWare.Core.Interfaces; + +namespace TestWare.Samples.Selenoid.Web.POM.Menu; + +/// +/// Initializes a new instance of the interface class that contains +/// all the methods that can be used at Steps level. +/// +public interface IMenuPage : ITestWareComponent +{ + void ClickLogoutButton(); + void ClickOpenMenuButton(); +} diff --git a/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Menu/MenuPage.cs b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Menu/MenuPage.cs new file mode 100644 index 00000000..3c953f28 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/POM/SwagLabs/Menu/MenuPage.cs @@ -0,0 +1,30 @@ +using OpenQA.Selenium; +using TestWare.Engines.Common.Extras; +using TestWare.Engines.Selenoid.Factory; +using TestWare.Engines.Selenoid.Pages; + +namespace TestWare.Samples.Selenoid.Web.POM.Menu; + +/// +/// Initializes a new instance of the class that contains +/// all the elements and the interaction methods that will be exposed at it's Interface class. +/// +public class MenuPage : WebPage, IMenuPage +{ + [FindsBy(How = How.Id, Using = "react-burger-menu-btn")] + private IWebElement OpenMenuButton { get; set; } + + [FindsBy(How = How.Id, Using = "logout_sidebar_link")] + private IWebElement LogoutButton { get; set; } + + public MenuPage(IWebDriver driver) + : base(driver) + { + } + + public void ClickOpenMenuButton() + => ClickElement(OpenMenuButton); + + public void ClickLogoutButton() + => ClickElement(LogoutButton); +} diff --git a/samples/TestWare.Samples.Selenoid.Web/Selenoid/browsers.json b/samples/TestWare.Samples.Selenoid.Web/Selenoid/browsers.json new file mode 100644 index 00000000..c4fb8d4c --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/Selenoid/browsers.json @@ -0,0 +1,65 @@ +{ + "chrome": { + "default": "111.0", + "versions": { + "111.0": { + "image": "selenoid/chrome:111.0", + "port": "4444", + "volumes": ["/c/temp/downloads:/home/selenium/Downloads"], + "path": "/", + "tmpfs": { + "/tmp": "size=128m" + } + }, + "94.0": { + "image": "selenoid/chrome:94.0", + "port": "4444", + "volumes": ["/c/temp/downloads:/home/selenium/Downloads"], + "path": "/", + "tmpfs": { + "/tmp": "size=128m" + } + } + } + }, + "firefox": { + "default": "110.0", + "versions": { + "110.0": { + "image": "selenoid/firefox:110.0", + "port": "4444", + "volumes": ["/c/temp/downloads:/home/selenium/Downloads"], + "path": "/wd/hub", + "tmpfs": { + "/tmp": "size=128m" + } + }, + "92.0": { + "image": "selenoid/firefox:92.0", + "port": "4444", + "volumes": ["/c/temp/downloads:/home/selenium/Downloads"], + "path": "/wd/hub", + "tmpfs": { + "/tmp": "size=128m" + } + } + } + }, + "MicrosoftEdge": { + "default": "111.0", + "versions": { + "111.0": { + "image": "browsers/edge:111.0", + "port": "4444", + "volumes": ["/c/temp/downloads:/home/selenium/Downloads"], + "path": "/" + }, + "94.0": { + "image": "browsers/edge:94.0", + "port": "4444", + "volumes": ["/c/temp/downloads:/home/selenium/Downloads"], + "path": "/" + } + } + } +} diff --git a/samples/TestWare.Samples.Selenoid.Web/Selenoid/docker-compose.yml b/samples/TestWare.Samples.Selenoid.Web/Selenoid/docker-compose.yml new file mode 100644 index 00000000..9daccaf3 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/Selenoid/docker-compose.yml @@ -0,0 +1,28 @@ +version: '3' +services: + selenoid: + restart: always + network_mode: bridge + image: aerokube/selenoid:latest-release + volumes: + - ".:/etc/selenoid" + - "/var/run/docker.sock:/var/run/docker.sock" + - "./video:/opt/selenoid/video" + - "./logs:/opt/selenoid/logs" + environment: + - OVERRIDE_VIDEO_OUTPUT_DIR=./logs + command: ["-conf", "/etc/selenoid/browsers.json", "-video-output-dir", "/opt/selenoid/video", "-log-output-dir", "/opt/selenoid/logs"] + ports: + - "4444:4444" + + selenoid-ui: + restart: always + image: "aerokube/selenoid-ui:latest-release" + network_mode: bridge + depends_on: + - selenoid + links: + - selenoid + ports: + - "8888:8080" + command: ["--selenoid-uri", "http://selenoid:4444"] \ No newline at end of file diff --git a/samples/TestWare.Samples.Selenoid.Web/Selenoid/start-selenoid-docker.ps1 b/samples/TestWare.Samples.Selenoid.Web/Selenoid/start-selenoid-docker.ps1 new file mode 100644 index 00000000..ef53fd45 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/Selenoid/start-selenoid-docker.ps1 @@ -0,0 +1,13 @@ +Write-Host "Stopping selenoid and selenoid-ui containers" -ForegroundColor DarkGreen +docker-compose down + +Write-Host "Downloading browser images...." -ForegroundColor DarkGreen +$browsers = Get-Content .\browsers.json | ConvertFrom-Json +foreach($browser in $browsers.PsObject.Properties.Value) { + foreach($version in $browser.versions.PsObject.Properties.Value) { + docker pull $version.image + } +} + +Write-Host "Starting selenoid and selenoid-ui containers" -ForegroundColor DarkGreen +docker-compose up -d \ No newline at end of file diff --git a/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/InventorySteps.cs b/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/InventorySteps.cs new file mode 100644 index 00000000..18f44adf --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/InventorySteps.cs @@ -0,0 +1,25 @@ +using TestWare.Core; +using TestWare.Samples.Selenoid.Web.POM.Inventory; + +namespace TestWare.Samples.Selenoid.Web.StepDefinitions; + +/// +/// Initializes a new instance of the class that contains +/// all the step action methods that can be used at Feature level. +/// +[Binding] +public sealed class InventorySteps +{ + private readonly IInventoryPage inventoryPage; + + public InventorySteps() + { + inventoryPage = ContainerManager.GetTestWareComponent(); + } + + [Then(@"the user can login")] + public void ThenTheUserCanLogin() + { + inventoryPage.CheckUserIsAtInventory(); + } +} diff --git a/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/LoginSteps.cs b/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/LoginSteps.cs new file mode 100644 index 00000000..c43b759d --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/LoginSteps.cs @@ -0,0 +1,53 @@ +using TestWare.Core; +using TestWare.Samples.Selenoid.Web.POM.Login; + +namespace TestWare.Samples.Selenoid.Web.StepDefinitions; + +/// +/// Initializes a new instance of the class that contains +/// all the step action methods that can be used at Feature level. +/// +[Binding] +public sealed class LoginSteps +{ + private readonly ILoginPage loginPage; + + public LoginSteps() + { + loginPage = ContainerManager.GetTestWareComponent(); + } + + [Given(@"the user enters username '([^']*)'")] + public void GivenTheUserEntersUsername(string userName) + { + loginPage.EnterUserName(userName); + } + + [Given(@"the user enters password '([^']*)'")] + public void GivenTheUserEntersValidPassword(string password) + { + loginPage.EnterUserPassword(password); + } + + [Given(@"user '([^']*)' is logged with '([^']*)' into SwagLabs")] + public void GivenUserIsLogedWithIntoSwagLabs(string userName, string password) + { + GivenTheUserEntersUsername(userName); + GivenTheUserEntersValidPassword(password); + WhenTheUserClicksSubmit(); + } + + + [Given(@"the user clicks submit")] + [When(@"the user clicks submit")] + public void WhenTheUserClicksSubmit() + { + loginPage.ClickLoginButton(); + } + + [Then(@"the user is at Login page")] + public void UserIsAtLoginPage() + { + loginPage.CheckUserIsAtLoginpage(); + } +} diff --git a/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/MenuSteps.cs b/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/MenuSteps.cs new file mode 100644 index 00000000..492ec89a --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/MenuSteps.cs @@ -0,0 +1,26 @@ +using TestWare.Core; +using TestWare.Samples.Selenoid.Web.POM.Menu; + +namespace TestWare.Samples.Selenoid.Web.StepDefinitions; + +/// +/// Initializes a new instance of the class that contains +/// all the step action methods that can be used at Feature level. +/// +[Binding] +public sealed class MenuSteps +{ + private readonly IMenuPage menuPage; + + public MenuSteps() + { + menuPage = ContainerManager.GetTestWareComponent(); + } + + [When(@"the user clicks Logout button")] + public void WhenTheUserClicksLogoutButton() + { + menuPage.ClickOpenMenuButton(); + menuPage.ClickLogoutButton(); + } +} diff --git a/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/MultiplePageSteps.cs b/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/MultiplePageSteps.cs new file mode 100644 index 00000000..35fd511e --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/StepDefinitions/SwagLabs/MultiplePageSteps.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.Linq; +using TestWare.Core; +using TestWare.Samples.Selenoid.Web.POM.Inventory; +using TestWare.Samples.Selenoid.Web.POM.Login; +using TestWare.Samples.Selenoid.Web.POM.Menu; + +namespace TestWare.Samples.Selenoid.Web.StepDefinitions; + +/// +/// Initializes a new instance of the class that contains +/// all the step action methods that can be used at Feature level. +/// +[Binding] +public sealed class MultiplePageSteps +{ + private readonly IEnumerable loginPages; + private readonly IEnumerable inventoryPages; + + public MultiplePageSteps(FeatureContext featureContext, ScenarioContext scenarioContext) + { + var tags = featureContext.FeatureInfo.Tags.Concat(scenarioContext.ScenarioInfo.Tags); + loginPages = ContainerManager.GetTestWareComponents(tags); + inventoryPages = ContainerManager.GetTestWareComponents(tags); + } + + [Given(@"the user enters username '([^']*)' on all")] + public void GivenTheUserEntersUsernameOnAll(string userName) + { + loginPages.ToList().ForEach(x => x.EnterUserName(userName)); + } + + [Given(@"the user enters password '([^']*)' on all")] + public void GivenTheUserEntersValidPasswordOnAll(string password) + { + loginPages.ToList().ForEach(x => x.EnterUserPassword(password)); + } + + [Given(@"the user clicks submit on all")] + [When(@"the user clicks submit on all")] + public void WhenTheUserClicksSubmitOnAll() + { + loginPages.ToList().ForEach(x => x.ClickLoginButton()); + } + + [Given(@"the user can login on all")] + [Then(@"the user can login on all")] + public void ThenTheUserCanLoginOnAll() + { + inventoryPages.ToList().ForEach(x => x.CheckUserIsAtInventory()); + } + + [When(@"the user clicks Logout button on '([^']*)'")] + public static void WhenTheUserClicksLogoutButtonOn(string browser) + { + var menuPage = ContainerManager.GetTestWareComponent(browser); + menuPage.ClickOpenMenuButton(); + menuPage.ClickLogoutButton(); + } + + [Then(@"the user is at Login page on '([^']*)'")] + public static void ThenTheUserIsAtLoginPageOn(string browser) + { + var loginPage = ContainerManager.GetTestWareComponent(browser); + loginPage.CheckUserIsAtLoginpage(); + } +} diff --git a/samples/TestWare.Samples.Selenoid.Web/TestConfiguration.Web.json b/samples/TestWare.Samples.Selenoid.Web/TestConfiguration.Web.json new file mode 100644 index 00000000..d96ad277 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/TestConfiguration.Web.json @@ -0,0 +1,52 @@ +{ + "Configurations": [ + { + "Tag": "RemoteDriver", + "Capabilities": [ + { + "Name": "Chrome", + "Uri": "http://localhost:4444/wd/hub", + "BrowserName": "Chrome", + "BrowserVersion": "111.0", + "Resolution": "1920x1080x24", + "EnableLog": false, + "EnableVnc": true, + "EnableVideo": false, + "CommandTimeOutInMinutes": 5, + "Arguments": [ + "--start-maximized" + ] + }, + { + "Name": "Firefox", + "Uri": "http://localhost:4444/wd/hub", + "BrowserName": "Firefox", + "BrowserVersion": "110.0", + "Resolution": "1920x1080x24", + "EnableLog": false, + "EnableVnc": true, + "EnableVideo": false, + "CommandTimeOutInMinutes": 5, + "Arguments": [ + "--start-maximized" + ] + }, + { + "Name": "Edge", + "Uri": "http://localhost:4444/wd/hub", + "BrowserName": "Edge", + "BrowserVersion": "111.0", + "Resolution": "1920x1080x24", + "EnableLog": false, + "EnableVnc": true, + "EnableVideo": false, + "CommandTimeOutInMinutes": 5, + "Arguments": [ + "--start-maximized" + ] + } + ] + } + ], + "TestResultPath": "C:\\workspace\\ERNI\\results\\" +} \ No newline at end of file diff --git a/samples/TestWare.Samples.Selenoid.Web/TestWare.Samples.Selenoid.Web.csproj b/samples/TestWare.Samples.Selenoid.Web/TestWare.Samples.Selenoid.Web.csproj new file mode 100644 index 00000000..26ee5272 --- /dev/null +++ b/samples/TestWare.Samples.Selenoid.Web/TestWare.Samples.Selenoid.Web.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + + + + + + + Always + + + + diff --git a/samples/TestWare.Samples.WinAppDriver/POM/Calculator/CalculatorPage.cs b/samples/TestWare.Samples.WinAppDriver/POM/Calculator/CalculatorPage.cs index 8f597e1d..1b758ba6 100644 --- a/samples/TestWare.Samples.WinAppDriver/POM/Calculator/CalculatorPage.cs +++ b/samples/TestWare.Samples.WinAppDriver/POM/Calculator/CalculatorPage.cs @@ -1,6 +1,6 @@ using OpenQA.Selenium; using OpenQA.Selenium.Appium; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.WinAppDriver.Factory; using TestWare.Engines.Appium.WinAppDriver.Pages; diff --git a/samples/TestWare.Samples.WinAppDriver/POM/Notepad/NotepadPage.cs b/samples/TestWare.Samples.WinAppDriver/POM/Notepad/NotepadPage.cs index c33e6cee..c8e7c05a 100644 --- a/samples/TestWare.Samples.WinAppDriver/POM/Notepad/NotepadPage.cs +++ b/samples/TestWare.Samples.WinAppDriver/POM/Notepad/NotepadPage.cs @@ -1,5 +1,5 @@ using OpenQA.Selenium; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.WinAppDriver.Factory; using TestWare.Engines.Appium.WinAppDriver.Pages; diff --git a/samples/TestWare.Samples.WinAppDriver/POM/WindowsCalculator/WindowsCalculatorPage.cs b/samples/TestWare.Samples.WinAppDriver/POM/WindowsCalculator/WindowsCalculatorPage.cs index f0c2e66e..91df494a 100644 --- a/samples/TestWare.Samples.WinAppDriver/POM/WindowsCalculator/WindowsCalculatorPage.cs +++ b/samples/TestWare.Samples.WinAppDriver/POM/WindowsCalculator/WindowsCalculatorPage.cs @@ -1,6 +1,6 @@ using OpenQA.Selenium.Appium; using OpenQA.Selenium; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.WinAppDriver.Factory; using TestWare.Engines.Appium.WinAppDriver.Pages; using Humanizer; diff --git a/samples/TestWare.Samples.WinAppDriver/TestWare.Samples.WinAppDriver.Desktop.csproj b/samples/TestWare.Samples.WinAppDriver/TestWare.Samples.WinAppDriver.Desktop.csproj index 9e03f0a5..f678d77a 100644 --- a/samples/TestWare.Samples.WinAppDriver/TestWare.Samples.WinAppDriver.Desktop.csproj +++ b/samples/TestWare.Samples.WinAppDriver/TestWare.Samples.WinAppDriver.Desktop.csproj @@ -6,13 +6,13 @@ + - - + - + diff --git a/src/Engines/TestWare.Engines.Appium/AppiumManager.cs b/src/Engines/TestWare.Engines.Appium/AppiumManager.cs index b044a99a..637b7b25 100644 --- a/src/Engines/TestWare.Engines.Appium/AppiumManager.cs +++ b/src/Engines/TestWare.Engines.Appium/AppiumManager.cs @@ -68,7 +68,7 @@ public string CollectEvidence(string destinationPath, string evidenceName) } var screenshot = ((ITakesScreenshot)appiumDriver).GetScreenshot(); - screenshot.SaveAsFile(Path.Combine(destinationPath, $"{evidenceName}.png"), ScreenshotImageFormat.Png); + screenshot.SaveAsFile(Path.Combine(destinationPath, $"{evidenceName}.png")); } catch { diff --git a/src/Engines/TestWare.Engines.Appium/Extras/ByFactory.cs b/src/Engines/TestWare.Engines.Appium/Extras/ByFactory.cs deleted file mode 100644 index 10fc83ca..00000000 --- a/src/Engines/TestWare.Engines.Appium/Extras/ByFactory.cs +++ /dev/null @@ -1,67 +0,0 @@ -#nullable enable -using System.Globalization; -using System.Reflection; -using OpenQA.Selenium; -using OpenQA.Selenium.Appium; - -namespace TestWare.Engines.Appium.Extras; - -/// -/// Provides instances of the object to the attributes. -/// -internal static class ByFactory -{ - /// - /// Gets an instance of the class based on the specified attribute. - /// - /// The describing how to find the element. - /// An instance of the class. - public static By From(FindsByAttribute attribute) - { - var how = attribute.How; - var usingValue = attribute.Using; - switch (how) - { - case How.Id: - return MobileBy.Id(usingValue); - case How.Name: - return MobileBy.Name(usingValue); - case How.TagName: - return MobileBy.TagName(usingValue); - case How.ClassName: - return MobileBy.ClassName(usingValue); - case How.CssSelector: - return By.CssSelector(usingValue); - case How.LinkText: - return By.LinkText(usingValue); - case How.PartialLinkText: - return By.PartialLinkText(usingValue); - case How.XPath: - return By.XPath(usingValue); - case How.AccessibilityId: - return MobileBy.AccessibilityId(usingValue); - case How.Custom: - if (attribute.CustomFinderType == null) - { - throw new ArgumentException("Cannot use How.Custom without supplying a custom finder type"); - } - - if (!attribute.CustomFinderType.IsSubclassOf(typeof(By))) - { - throw new ArgumentException("Custom finder type must be a descendent of the By class"); - } - - ConstructorInfo? ctor = attribute.CustomFinderType.GetConstructor(new Type[] { typeof(string) }); - if (ctor == null) - { - throw new ArgumentException("Custom finder type must expose a public constructor with a string argument"); - } - - By finder = (By)ctor.Invoke(new object?[] { usingValue }); - - return finder; - } - - throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Did not know how to construct How from how {0}, using {1}", how, usingValue)); - } -} diff --git a/src/Engines/TestWare.Engines.Appium/Extras/FindsByAttribute.cs b/src/Engines/TestWare.Engines.Appium/Extras/FindsByAttribute.cs deleted file mode 100644 index f0a2bcd9..00000000 --- a/src/Engines/TestWare.Engines.Appium/Extras/FindsByAttribute.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.ComponentModel; -using OpenQA.Selenium; - -namespace TestWare.Engines.Appium.Extras; - -public class FindsByAttribute : TestWare.Engines.Selenium.Extras.FindsByAttribute -{ - [DefaultValue(How.Id)] - new public How How { get; set; } - - public override By Finder - { - get - { - if (this.finder == null) - { - this.finder = ByFactory.From(this); - } - - return this.finder; - } - } -} diff --git a/src/Engines/TestWare.Engines.Appium/Extras/PageFactory.cs b/src/Engines/TestWare.Engines.Appium/Extras/PageFactory.cs deleted file mode 100644 index f4b74bae..00000000 --- a/src/Engines/TestWare.Engines.Appium/Extras/PageFactory.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace TestWare.Engines.Appium.Extras; - -public class PageFactory : Selenium.Extras.PageFactory -{ -} diff --git a/src/Engines/TestWare.Engines.Appium/Pages/MobilePage.cs b/src/Engines/TestWare.Engines.Appium/Pages/MobilePage.cs index 59e89a33..b1a479a6 100644 --- a/src/Engines/TestWare.Engines.Appium/Pages/MobilePage.cs +++ b/src/Engines/TestWare.Engines.Appium/Pages/MobilePage.cs @@ -2,7 +2,7 @@ using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.MultiTouch; using System.Drawing; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.Factory; using TestWare.Engines.Selenium.Pages; diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/AbstractFindsByAttribute.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/AbstractFindsByAttribute.cs similarity index 99% rename from src/Engines/TestWare.Engines.Selenium/Extras/AbstractFindsByAttribute.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/AbstractFindsByAttribute.cs index 0f1f81da..828a5701 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/AbstractFindsByAttribute.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/AbstractFindsByAttribute.cs @@ -1,7 +1,7 @@ using System.ComponentModel; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Base class for attributes to mark elements with methods by which to find a corresponding element on the page. diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/ByAll.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByAll.cs similarity index 98% rename from src/Engines/TestWare.Engines.Selenium/Extras/ByAll.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByAll.cs index 7dda4a55..2790fa5c 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/ByAll.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByAll.cs @@ -5,7 +5,7 @@ using System.Text; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Mechanism used to locate elements within a document using a series of lookups. This class will diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/ByChained.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByChained.cs similarity index 98% rename from src/Engines/TestWare.Engines.Selenium/Extras/ByChained.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByChained.cs index d302c713..e3acc07e 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/ByChained.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByChained.cs @@ -5,7 +5,7 @@ using System.Text; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Mechanism used to locate elements within a document using a series of other lookups. This class diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/ByFactory.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByFactory.cs similarity index 93% rename from src/Engines/TestWare.Engines.Selenium/Extras/ByFactory.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByFactory.cs index 3cda7f11..cb5174f6 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/ByFactory.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByFactory.cs @@ -2,8 +2,9 @@ using System.Globalization; using System.Reflection; using OpenQA.Selenium; +using OpenQA.Selenium.Appium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Provides instances of the object to the attributes. @@ -36,7 +37,9 @@ public static By From(FindsByAttribute attribute) case How.PartialLinkText: return By.PartialLinkText(usingValue); case How.XPath: - return By.XPath(usingValue); + return By.XPath(usingValue); + case How.AccessibilityId: + return MobileBy.AccessibilityId(usingValue); case How.Custom: if (attribute.CustomFinderType == null) { diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/ByIdOrName.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByIdOrName.cs similarity index 98% rename from src/Engines/TestWare.Engines.Selenium/Extras/ByIdOrName.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByIdOrName.cs index 36e875e1..9d2b2343 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/ByIdOrName.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ByIdOrName.cs @@ -3,7 +3,7 @@ using System.Globalization; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Finds element when the id or the name attribute has the specified value. diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/CacheLookupAttribute.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/CacheLookupAttribute.cs similarity index 87% rename from src/Engines/TestWare.Engines.Selenium/Extras/CacheLookupAttribute.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/CacheLookupAttribute.cs index 95e1a73f..07c5edf0 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/CacheLookupAttribute.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/CacheLookupAttribute.cs @@ -1,5 +1,5 @@  -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Marks the element so that lookups to the browser page are cached. This class cannot be inherited. diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/DefaultElementLocator.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/DefaultElementLocator.cs similarity index 98% rename from src/Engines/TestWare.Engines.Selenium/Extras/DefaultElementLocator.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/DefaultElementLocator.cs index cb9cd5fe..d422341d 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/DefaultElementLocator.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/DefaultElementLocator.cs @@ -3,7 +3,7 @@ using System.Collections.ObjectModel; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// A default locator for elements for use with the . This locator diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/DefaultPageObjectMemberDecorator.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/DefaultPageObjectMemberDecorator.cs similarity index 99% rename from src/Engines/TestWare.Engines.Selenium/Extras/DefaultPageObjectMemberDecorator.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/DefaultPageObjectMemberDecorator.cs index 7fd57faf..4ab22a84 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/DefaultPageObjectMemberDecorator.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/DefaultPageObjectMemberDecorator.cs @@ -5,7 +5,7 @@ using OpenQA.Selenium; using TestWare.Engines.Selenium.Extras.MemberBuilders; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Default decorator determining how members of a class which represent elements in a Page Object diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/ExpectedConditions.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ExpectedConditions.cs similarity index 99% rename from src/Engines/TestWare.Engines.Selenium/Extras/ExpectedConditions.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ExpectedConditions.cs index 7a80173a..040bfa97 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/ExpectedConditions.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/ExpectedConditions.cs @@ -3,7 +3,7 @@ using System.Text.RegularExpressions; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Supplies a set of common conditions that can be waited for using . @@ -73,7 +73,7 @@ public static Func UrlMatches(string regex) return (driver) => { var currentUrl = driver.Url; - var pattern = new Regex(regex, RegexOptions.IgnoreCase); + var pattern = new Regex(regex, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(10)); var match = pattern.Match(currentUrl); return match.Success; }; diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/FindsByAllAttribute.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsByAllAttribute.cs similarity index 95% rename from src/Engines/TestWare.Engines.Selenium/Extras/FindsByAllAttribute.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsByAllAttribute.cs index 168bb0f2..bd88807c 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/FindsByAllAttribute.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsByAllAttribute.cs @@ -1,5 +1,5 @@  -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Marks elements to indicate that found elements should match the criteria of diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/FindsByAttribute.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsByAttribute.cs similarity index 98% rename from src/Engines/TestWare.Engines.Selenium/Extras/FindsByAttribute.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsByAttribute.cs index 8853dc6e..cb3467b8 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/FindsByAttribute.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsByAttribute.cs @@ -3,7 +3,7 @@ using System.ComponentModel; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Marks program elements with methods by which to find a corresponding element on the page. Used diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/FindsBySequenceAttribute.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsBySequenceAttribute.cs similarity index 96% rename from src/Engines/TestWare.Engines.Selenium/Extras/FindsBySequenceAttribute.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsBySequenceAttribute.cs index 92831391..ed1b14c0 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/FindsBySequenceAttribute.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/FindsBySequenceAttribute.cs @@ -1,6 +1,6 @@  -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Marks elements to indicate that each on the field or diff --git a/src/Engines/TestWare.Engines.Appium/Extras/How.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/How.cs similarity index 96% rename from src/Engines/TestWare.Engines.Appium/Extras/How.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/How.cs index aa0424b2..a3d52fda 100644 --- a/src/Engines/TestWare.Engines.Appium/Extras/How.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/How.cs @@ -1,4 +1,5 @@ -namespace TestWare.Engines.Appium.Extras; + +namespace TestWare.Engines.Common.Extras; /// /// Provides the lookup methods for the FindsBy attribute (for using in PageObjects) diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/IElementLocator.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/IElementLocator.cs similarity index 96% rename from src/Engines/TestWare.Engines.Selenium/Extras/IElementLocator.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/IElementLocator.cs index bbd210b5..8fe81455 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/IElementLocator.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/IElementLocator.cs @@ -2,7 +2,7 @@ using System.Collections.ObjectModel; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Interface describing how elements are to be located by a . diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/IPageObjectMemberDecorator.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/IPageObjectMemberDecorator.cs similarity index 94% rename from src/Engines/TestWare.Engines.Selenium/Extras/IPageObjectMemberDecorator.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/IPageObjectMemberDecorator.cs index 9c45442d..14e3246b 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/IPageObjectMemberDecorator.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/IPageObjectMemberDecorator.cs @@ -2,7 +2,7 @@ using System.Reflection; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Interface describing how members of a class which represent elements in a Page Object diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/IMemberBuilder.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/IMemberBuilder.cs similarity index 97% rename from src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/IMemberBuilder.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/IMemberBuilder.cs index 59ad7312..fd0678ea 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/IMemberBuilder.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/IMemberBuilder.cs @@ -18,6 +18,7 @@ using System.Diagnostics.CodeAnalysis; using OpenQA.Selenium; +using TestWare.Engines.Common.Extras; namespace TestWare.Engines.Selenium.Extras.MemberBuilders; diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WebElementBuilder.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WebElementBuilder.cs similarity index 97% rename from src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WebElementBuilder.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WebElementBuilder.cs index 770b365c..e6e6d462 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WebElementBuilder.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WebElementBuilder.cs @@ -18,6 +18,7 @@ using System.Diagnostics.CodeAnalysis; using OpenQA.Selenium; +using TestWare.Engines.Common.Extras; namespace TestWare.Engines.Selenium.Extras.MemberBuilders; diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WebElementListBuilder.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WebElementListBuilder.cs similarity index 94% rename from src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WebElementListBuilder.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WebElementListBuilder.cs index 7ac927e4..decff291 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WebElementListBuilder.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WebElementListBuilder.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using OpenQA.Selenium; +using TestWare.Engines.Common.Extras; namespace TestWare.Engines.Selenium.Extras.MemberBuilders; diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WrappedElementBuilder.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WrappedElementBuilder.cs similarity index 94% rename from src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WrappedElementBuilder.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WrappedElementBuilder.cs index 406245ea..750aa20e 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WrappedElementBuilder.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WrappedElementBuilder.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using OpenQA.Selenium; +using TestWare.Engines.Common.Extras; namespace TestWare.Engines.Selenium.Extras.MemberBuilders; diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WrappedElementListBuilder.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WrappedElementListBuilder.cs similarity index 96% rename from src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WrappedElementListBuilder.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WrappedElementListBuilder.cs index 8b9f088c..175bfa9a 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/MemberBuilders/WrappedElementListBuilder.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/MemberBuilders/WrappedElementListBuilder.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using OpenQA.Selenium; +using TestWare.Engines.Common.Extras; namespace TestWare.Engines.Selenium.Extras.MemberBuilders; diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/PageFactory.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/PageFactory.cs similarity index 99% rename from src/Engines/TestWare.Engines.Selenium/Extras/PageFactory.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/PageFactory.cs index 391fda02..4fc46dfb 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/PageFactory.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/PageFactory.cs @@ -3,7 +3,7 @@ using System.Reflection; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Provides the ability to produce Page Objects modeling a page. This class cannot be inherited. diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/RetryingElementLocator.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/RetryingElementLocator.cs similarity index 99% rename from src/Engines/TestWare.Engines.Selenium/Extras/RetryingElementLocator.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/RetryingElementLocator.cs index 3acbf4a1..86859b52 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/RetryingElementLocator.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/RetryingElementLocator.cs @@ -3,7 +3,7 @@ using System.Collections.ObjectModel; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// A locator for elements for use with the that retries locating diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/WebDriverObjectProxy.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebDriverObjectProxy.cs similarity index 96% rename from src/Engines/TestWare.Engines.Selenium/Extras/WebDriverObjectProxy.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebDriverObjectProxy.cs index 30885e29..28f46984 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/WebDriverObjectProxy.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebDriverObjectProxy.cs @@ -1,6 +1,6 @@ using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Represents a base proxy class for objects used with the PageFactory. diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/WebElementEnumerable.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementEnumerable.cs similarity index 96% rename from src/Engines/TestWare.Engines.Selenium/Extras/WebElementEnumerable.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementEnumerable.cs index 0e27ede9..2841bf49 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/WebElementEnumerable.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementEnumerable.cs @@ -1,6 +1,6 @@ using TestWare.Engines.Selenium.Extras; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Due to Linq optimized execution in dotnet core for IList, some methods lead to multiple elements retrieval. diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/WebElementListProxy.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementListProxy.cs similarity index 97% rename from src/Engines/TestWare.Engines.Selenium/Extras/WebElementListProxy.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementListProxy.cs index 9d3376ba..92834ab6 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/WebElementListProxy.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementListProxy.cs @@ -3,7 +3,7 @@ using System.Collections; using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Represents a proxy class for a list of elements to be used with the PageFactory. diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/WebElementProxy.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementProxy.cs similarity index 94% rename from src/Engines/TestWare.Engines.Selenium/Extras/WebElementProxy.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementProxy.cs index 3448a478..5c71fde4 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/WebElementProxy.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WebElementProxy.cs @@ -5,7 +5,7 @@ using OpenQA.Selenium.Interactions.Internal; using OpenQA.Selenium.Internal; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Intercepts the request to a single @@ -63,9 +63,6 @@ public IWebElement WrappedElement public string GetCssValue(string propertyName) => WrappedElement.GetCssValue(propertyName); - [Obsolete ("Deprecated on IWebElement")] - public string GetProperty(string propertyName) => WrappedElement.GetProperty(propertyName); - public void SendKeys(string text) => WrappedElement.SendKeys(text); public void Submit() => WrappedElement.Submit(); diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/WrapsElementFactory.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WrapsElementFactory.cs similarity index 96% rename from src/Engines/TestWare.Engines.Selenium/Extras/WrapsElementFactory.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WrapsElementFactory.cs index 55906ade..5a663dbd 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/WrapsElementFactory.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WrapsElementFactory.cs @@ -1,6 +1,6 @@ using OpenQA.Selenium; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; internal static class WrapsElementFactory { diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/WrapsElementListProxy.cs b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WrapsElementListProxy.cs similarity index 97% rename from src/Engines/TestWare.Engines.Selenium/Extras/WrapsElementListProxy.cs rename to src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WrapsElementListProxy.cs index 8e9cc2e3..fc9736d0 100644 --- a/src/Engines/TestWare.Engines.Selenium/Extras/WrapsElementListProxy.cs +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/Extras/WrapsElementListProxy.cs @@ -4,7 +4,7 @@ using OpenQA.Selenium; using TestWare.Engines.Selenium.Extras; -namespace TestWare.Engines.Selenium.Extras; +namespace TestWare.Engines.Common.Extras; /// /// Represents a proxy class for a list of elements to be used with the PageFactory. diff --git a/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/TestWare.Engines.Common.csproj b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/TestWare.Engines.Common.csproj new file mode 100644 index 00000000..3797629f --- /dev/null +++ b/src/Engines/TestWare.Engines.Common/TestWare.Engines.Common/TestWare.Engines.Common.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + enable + enable + + + + + + + + + diff --git a/src/Engines/TestWare.Engines.Selenium/Configuration/Capabilities.cs b/src/Engines/TestWare.Engines.Selenium/Configuration/Capabilities.cs index 24f427a8..5c01917f 100644 --- a/src/Engines/TestWare.Engines.Selenium/Configuration/Capabilities.cs +++ b/src/Engines/TestWare.Engines.Selenium/Configuration/Capabilities.cs @@ -1,9 +1,9 @@ -using TestWare.Engines.Selenium.Factory; - -namespace TestWare.Engines.Selenium.Configuration; +using TestWare.Engines.Selenium.Factory; + +namespace TestWare.Engines.Selenium.Configuration; -internal class Capabilities -{ +internal class Capabilities +{ public string Name { get; set; } public string Path { get; set; } @@ -13,10 +13,11 @@ internal class Capabilities public string BaseUrl { get; set; } public int CommandTimeOutInMinutes { get; set; } + public IEnumerable Arguments { get; set; } = Enumerable.Empty(); - - public SupportedBrowsers GetDriver() - { - return Enum.Parse(Driver, true); + + public SupportedBrowsers GetDriver() + { + return Enum.Parse(Driver, true); } -} +} diff --git a/src/Engines/TestWare.Engines.Selenium/Extras/How.cs b/src/Engines/TestWare.Engines.Selenium/Extras/How.cs deleted file mode 100644 index 4ff44fce..00000000 --- a/src/Engines/TestWare.Engines.Selenium/Extras/How.cs +++ /dev/null @@ -1,53 +0,0 @@ - -namespace TestWare.Engines.Selenium.Extras; - -/// -/// Provides the lookup methods for the FindsBy attribute (for using in PageObjects) -/// -public enum How -{ - /// - /// Finds by - /// - Id, - - /// - /// Finds by - /// - Name, - - /// - /// Finds by - /// - TagName, - - /// - /// Finds by - /// - ClassName, - - /// - /// Finds by - /// - CssSelector, - - /// - /// Finds by - /// - LinkText, - - /// - /// Finds by - /// - PartialLinkText, - - /// - /// Finds by - /// - XPath, - - /// - /// Finds by a custom implementation. - /// - Custom -} diff --git a/src/Engines/TestWare.Engines.Selenium/Factory/ConfigurationTags.cs b/src/Engines/TestWare.Engines.Selenium/Factory/ConfigurationTags.cs index 680cc5df..26a67ffa 100644 --- a/src/Engines/TestWare.Engines.Selenium/Factory/ConfigurationTags.cs +++ b/src/Engines/TestWare.Engines.Selenium/Factory/ConfigurationTags.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace TestWare.Engines.Selenium.Factory; +namespace TestWare.Engines.Selenium.Factory; public enum ConfigurationTags { diff --git a/src/Engines/TestWare.Engines.Selenium/Pages/PageBase.cs b/src/Engines/TestWare.Engines.Selenium/Pages/PageBase.cs index d601ca2c..fd0eb089 100644 --- a/src/Engines/TestWare.Engines.Selenium/Pages/PageBase.cs +++ b/src/Engines/TestWare.Engines.Selenium/Pages/PageBase.cs @@ -2,7 +2,7 @@ using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Support.UI; using TestWare.Core.Libraries; -using TestWare.Engines.Selenium.Extras; +using TestWare.Engines.Common.Extras; namespace TestWare.Engines.Selenium.Pages; diff --git a/src/Engines/TestWare.Engines.Selenium/Pages/WebPage.cs b/src/Engines/TestWare.Engines.Selenium/Pages/WebPage.cs index 3a5d7f86..2ed6ad7f 100644 --- a/src/Engines/TestWare.Engines.Selenium/Pages/WebPage.cs +++ b/src/Engines/TestWare.Engines.Selenium/Pages/WebPage.cs @@ -1,5 +1,5 @@ using OpenQA.Selenium; -using TestWare.Engines.Selenium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Selenium.Factory; namespace TestWare.Engines.Selenium.Pages; diff --git a/src/Engines/TestWare.Engines.Selenium/SeleniumManager.cs b/src/Engines/TestWare.Engines.Selenium/SeleniumManager.cs index 082e6029..164bfbb1 100644 --- a/src/Engines/TestWare.Engines.Selenium/SeleniumManager.cs +++ b/src/Engines/TestWare.Engines.Selenium/SeleniumManager.cs @@ -94,7 +94,7 @@ public string CollectEvidence(string destinationPath, string evidenceName) { var instanceName = ContainerManager.GetNameFromInstance(webDriver); var ss = ((ITakesScreenshot)webDriver).GetScreenshot(); - ss.SaveAsFile(Path.Combine(destinationPath, $"{evidenceName} - {instanceName}.png"), ScreenshotImageFormat.Png); + ss.SaveAsFile(Path.Combine(destinationPath, $"{evidenceName} - {instanceName}.png")); } catch (WebDriverException) { } diff --git a/src/Engines/TestWare.Engines.Selenium/TestWare.Engines.Selenium.csproj b/src/Engines/TestWare.Engines.Selenium/TestWare.Engines.Selenium.csproj index 36441f58..c0914bb9 100644 --- a/src/Engines/TestWare.Engines.Selenium/TestWare.Engines.Selenium.csproj +++ b/src/Engines/TestWare.Engines.Selenium/TestWare.Engines.Selenium.csproj @@ -7,11 +7,12 @@ - + + diff --git a/src/Engines/TestWare.Engines.Selenoid/Configuration/Capabilities.cs b/src/Engines/TestWare.Engines.Selenoid/Configuration/Capabilities.cs new file mode 100644 index 00000000..df136411 --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/Configuration/Capabilities.cs @@ -0,0 +1,34 @@ +using TestWare.Engines.Selenoid.Factory; + +namespace TestWare.Engines.Selenoid.Configuration; + +internal class Capabilities +{ + public string? Name { get; set; } + + public string? Uri { get; set; } + + public string? BrowserName { get; set; } + + public string? BrowserVersion { get; set; } + + public string? ScreenResolution { get; set; } + + public int CommandTimeOutInMinutes { get; set; } + + public bool EnableLog { get; set; } + + public bool EnableVnc { get; set; } + + public bool EnableVideo { get; set; } + + public IEnumerable Arguments { get; set; } = Enumerable.Empty(); + + public SupportedBrowsers GetDriver() + { + if (BrowserName == null) { + throw new NullReferenceException("BrowserName capability was null."); + } + return Enum.Parse(BrowserName, true); + } +} \ No newline at end of file diff --git a/src/Engines/TestWare.Engines.Selenoid/Factory/BrowserFactory.cs b/src/Engines/TestWare.Engines.Selenoid/Factory/BrowserFactory.cs new file mode 100644 index 00000000..22e87359 --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/Factory/BrowserFactory.cs @@ -0,0 +1,77 @@ +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using OpenQA.Selenium.Edge; +using OpenQA.Selenium.Firefox; +using OpenQA.Selenium.Remote; +using TestWare.Engines.Selenoid.Configuration; + +namespace TestWare.Engines.Selenoid.Factory; + +internal static class BrowserFactory +{ + private static string SELENOID_OPTIONS_KEY = "selenoid:options"; + + public static IWebDriver Create(Capabilities capabilities) + { + IWebDriver result = capabilities.GetDriver() switch + { + SupportedBrowsers.Chrome => CreateChromeDriver(capabilities), + SupportedBrowsers.Firefox => CreateFirefoxDriver(capabilities), + SupportedBrowsers.Edge => CreateEdgeDriver(capabilities), + SupportedBrowsers.Invalid => throw new NotImplementedException(), + _ => throw new NotSupportedException($"Browser type is invalid."), + }; + return result; + } + private static IWebDriver CreateChromeDriver(Capabilities capabilities) + { + ChromeOptions options = new(); + options.AddArguments(capabilities.Arguments); + options.AddAdditionalOption(SELENOID_OPTIONS_KEY, GenerateSelenoidCapabilities(capabilities)); + + if (capabilities.Uri == null) throw new ArgumentNullException(nameof(capabilities.Uri)); + return new RemoteWebDriver(new Uri(capabilities.Uri), options.ToCapabilities()); + } + + private static IWebDriver CreateFirefoxDriver(Capabilities capabilities) + { + FirefoxOptions options = new(); + options.AddArguments(capabilities.Arguments); + options.AddAdditionalOption(SELENOID_OPTIONS_KEY, GenerateSelenoidCapabilities(capabilities)); + + if (capabilities.Uri == null) throw new ArgumentNullException(nameof(capabilities.Uri)); + return new RemoteWebDriver(new Uri(capabilities.Uri), options.ToCapabilities()); + } + + private static IWebDriver CreateEdgeDriver(Capabilities capabilities) + { + EdgeOptions options = new(); + options.AddArguments(capabilities.Arguments); + options.AddAdditionalOption(SELENOID_OPTIONS_KEY, GenerateSelenoidCapabilities(capabilities)); + + if (capabilities.Uri == null) throw new ArgumentNullException(nameof(capabilities.Uri)); + return new RemoteWebDriver(new Uri(capabilities.Uri), options.ToCapabilities()); + } + + private static Dictionary GenerateSelenoidCapabilities(Capabilities capabilities) + { + if (capabilities.BrowserName == null || capabilities.BrowserVersion == null || capabilities.ScreenResolution == null) + { + throw new ArgumentNullException(nameof(capabilities)); + } + var browser = (SupportedBrowsers)Enum.Parse(typeof(SupportedBrowsers), capabilities.BrowserName); + var supportedBrowser = SupportedBrowsersHelper.GetBrowserName(browser) ?? throw new ArgumentNullException(nameof(browser)); + + return new Dictionary + { + ["browserName"] = supportedBrowser, + ["browserVersion"] = capabilities.BrowserVersion, + ["screenResolution"] = capabilities.ScreenResolution, + ["name"] = capabilities.BrowserName, + ["sessionTimeout"] = capabilities.CommandTimeOutInMinutes + "m", + ["enableLog"] = capabilities.EnableLog, + ["enableVnc"] = capabilities.EnableVnc, + ["enableVideo"] = capabilities.EnableVideo + }; + } +} diff --git a/src/Engines/TestWare.Engines.Selenoid/Factory/BrowserNameAttribute.cs b/src/Engines/TestWare.Engines.Selenoid/Factory/BrowserNameAttribute.cs new file mode 100644 index 00000000..9fb01c33 --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/Factory/BrowserNameAttribute.cs @@ -0,0 +1,23 @@ +namespace TestWare.Engines.Selenoid.Factory +{ + [AttributeUsage(AttributeTargets.Field)] + internal class BrowserNameAttribute : Attribute + { + private readonly string Name; + + public BrowserNameAttribute(string name) + { + this.Name = name; + } + + public static string GetPropertyName() + { + return "BrowserName"; + } + + public string GetValue() + { + return this.Name; + } + } +} diff --git a/src/Engines/TestWare.Engines.Selenoid/Factory/ConfigurationTags.cs b/src/Engines/TestWare.Engines.Selenoid/Factory/ConfigurationTags.cs new file mode 100644 index 00000000..c0c30056 --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/Factory/ConfigurationTags.cs @@ -0,0 +1,8 @@ +namespace TestWare.Engines.Selenoid.Factory; + +public enum ConfigurationTags +{ + none = 0, + remotedriver = 1, + multiwebdriver = 2 +} \ No newline at end of file diff --git a/src/Engines/TestWare.Engines.Selenoid/Factory/SupportedBrowsers.cs b/src/Engines/TestWare.Engines.Selenoid/Factory/SupportedBrowsers.cs new file mode 100644 index 00000000..e720ab90 --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/Factory/SupportedBrowsers.cs @@ -0,0 +1,27 @@ +using System.Reflection; + +namespace TestWare.Engines.Selenoid.Factory; + +public enum SupportedBrowsers +{ + Invalid = 0, + [BrowserName("chrome")] + Chrome = 1, + [BrowserName("firefox")] + Firefox = 2, + [BrowserName("MicrosoftEdge")] + Edge = 3 +} + +static class SupportedBrowsersHelper +{ + public static string? GetBrowserName(this SupportedBrowsers enumValue) + { + return enumValue + .GetType() + .GetMember(enumValue.ToString()) + .First()? + .GetCustomAttribute()? + .GetValue(); + } +} diff --git a/src/Engines/TestWare.Engines.Selenoid/Pages/PageBase.cs b/src/Engines/TestWare.Engines.Selenoid/Pages/PageBase.cs new file mode 100644 index 00000000..56e21892 --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/Pages/PageBase.cs @@ -0,0 +1,129 @@ +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; +using OpenQA.Selenium.Support.UI; +using TestWare.Core.Libraries; +using TestWare.Engines.Common.Extras; + +namespace TestWare.Engines.Selenoid.Pages; + +public abstract class PageBase +{ + protected const int TimeToWait = 15; + protected const int NumberOfRetries = 5; + + public IWebDriver? Driver { get; protected set; } + + private readonly TimeSpan RetryAttemp = TimeSpan.FromMilliseconds(200); + + protected void ClickElement(IWebElement element) + { + element = element ?? throw new ArgumentNullException(nameof(element), "Element to be clicked was null"); + + RetryPolicies.ExecuteActionWithRetries( + () => + { + this.WaitUntilElementIsClickable(element); + element.Click(); + }, + numberOfRetries: NumberOfRetries, + retryAttemp: RetryAttemp); + } + + protected void ClickInnerElement(IWebElement element) + { + element = element ?? throw new ArgumentNullException(nameof(element), "Element to be clicked was null"); + if (Driver == null) throw new ArgumentNullException(nameof(Driver)); + + RetryPolicies.ExecuteActionWithRetries( + () => + { + this.WaitUntilElementIsClickable(element); + var action = new Actions(Driver); + action.MoveToElement(element).Click().Perform(); + }, + numberOfRetries: NumberOfRetries, + retryAttemp: RetryAttemp); + } + + protected void DoubleClickElement(IWebElement element) + { + element = element ?? throw new ArgumentNullException(nameof(element), "Element to be double clicked was null"); + + RetryPolicies.ExecuteActionWithRetries( + () => + { + this.WaitUntilElementIsClickable(element); + new Actions(Driver).DoubleClick(element).Perform(); + }, + numberOfRetries: NumberOfRetries, + retryAttemp: RetryAttemp); + } + + protected void SendKeysElement(IWebElement element, string text) + => this.SendKeysElement(element, text, TimeToWait); + + protected void SendKeysElement(IWebElement element, string text, int timeToWait) + { + element = element ?? throw new ArgumentNullException(nameof(element), "Element to send keys was null"); + + RetryPolicies.ExecuteActionWithRetries( + () => + { + this.WaitUntilElementIsClickable(element, timeToWait); + element.SendKeys(text); + }, + numberOfRetries: NumberOfRetries, + retryAttemp: RetryAttemp); + } + + protected void ClearElementText(IWebElement element) + => this.ClearElementText(element, TimeToWait); + + protected void ClearElementText(IWebElement element, int timeToWait) + { + element = element ?? throw new ArgumentNullException(nameof(element), "Element to clear was null"); + + RetryPolicies.ExecuteActionWithRetries( + () => + { + this.WaitUntilElementIsClickable(element, timeToWait); + element.Clear(); + }, + numberOfRetries: NumberOfRetries, + retryAttemp: RetryAttemp); + } + + protected void WaitUntilElementIsClickable(IWebElement element) + => this.WaitUntilElementIsClickable(element, TimeToWait); + + protected void WaitUntilElementIsClickable(IWebElement element, int timeToWait) + { + if (Driver == null) throw new ArgumentNullException(nameof(Driver)); + var webDriverWait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeToWait)); + webDriverWait.Until(ExpectedConditions.ElementToBeClickable(element)); + } + + protected void WaitUntilElementIsVisible(By locator) + => this.WaitUntilElementIsVisible(locator, TimeToWait); + + protected void WaitUntilElementIsVisible(By locator, int timeToWait) + { + if (Driver == null) throw new ArgumentNullException(nameof(Driver)); + var webDriverWait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeToWait)); + webDriverWait.Until(ExpectedConditions.VisibilityOfAllElementsLocatedBy(locator)); + } + + protected void WaitUntilElementNotVisible(By locator, int secondsToWait) + { + if (Driver == null) throw new ArgumentNullException(nameof(Driver)); + Thread.Sleep(1000); + var webDriverWait = new WebDriverWait(Driver, TimeSpan.FromSeconds(secondsToWait)); + webDriverWait.Until(ExpectedConditions.InvisibilityOfElementLocated(locator)); + } + + protected static void ExecuteActionWithDelay(Action action, int secondsToDelayAction) + { + Thread.Sleep(secondsToDelayAction * 1000); + action.Invoke(); + } +} diff --git a/src/Engines/TestWare.Engines.Selenoid/Pages/WebPage.cs b/src/Engines/TestWare.Engines.Selenoid/Pages/WebPage.cs new file mode 100644 index 00000000..78a4fb9d --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/Pages/WebPage.cs @@ -0,0 +1,33 @@ +using OpenQA.Selenium; +using TestWare.Engines.Common.Extras; + +namespace TestWare.Engines.Selenoid.Pages; + +public abstract class WebPage : PageBase +{ + protected string? Url { get; set; } + + protected WebPage(IWebDriver driver) + { + Driver = driver; + PageFactory.InitElements(Driver, this); + } + + public void NavigateToUrl() + { + if (Url == null) throw new NullReferenceException("Url variable was null"); + Driver?.Navigate().GoToUrl(new Uri(Url)); + } + + protected string AcceptDialog() + => this.AcceptDialog(TimeToWait); + + protected string AcceptDialog(int timeToWait) + { + if (Driver == null) throw new NullReferenceException("Driver variable was null"); + IAlert alert = ExpectedConditions.AlertIsPresent().Invoke(Driver); + var content = alert.Text; + alert.Accept(); + return content; + } +} diff --git a/src/Engines/TestWare.Engines.Selenoid/SelenoidManager.cs b/src/Engines/TestWare.Engines.Selenoid/SelenoidManager.cs new file mode 100644 index 00000000..43a9b5da --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/SelenoidManager.cs @@ -0,0 +1,119 @@ +using Autofac; +using Autofac.Core.Registration; +using OpenQA.Selenium; +using TestWare.Core; +using TestWare.Core.Configuration; +using TestWare.Core.Interfaces; +using TestWare.Engines.Selenoid.Configuration; +using TestWare.Engines.Selenoid.Factory; + +namespace TestWare.Engines.Selenoid +{ + public class SelenoidManager : EngineManagerBase, IEngineManager + { + private const string _name = "Selenoid"; + + private static void RegisterSingle(IEnumerable tags, TestConfiguration testConfiguration) + { + var configName = Enum.GetName(ConfigurationTags.remotedriver)?.ToUpperInvariant(); + var capabilities = ConfigurationManager.GetCapabilities(testConfiguration, configName); + var singleCapability = capabilities.FirstOrDefault(x => tags.Contains(x?.Name?.ToUpperInvariant())); + if (singleCapability != null && !ContainerManager.ExistsType(singleCapability.GetType())) + { + var driver = BrowserFactory.Create(singleCapability); + ContainerManager.RegisterType(singleCapability.Name, driver); + } + else { + throw new NullReferenceException("No suitable capability was found."); + } + } + + private static void RegisterMultiple(IEnumerable tags, TestConfiguration testConfiguration) + { + var configName = Enum.GetName(ConfigurationTags.multiwebdriver)?.ToUpperInvariant(); + var capabilities = ConfigurationManager.GetCapabilities(testConfiguration, configName); + var multipleCapabilities = capabilities.Where(x => tags.Contains(x?.Name?.ToUpperInvariant())); + + if (!multipleCapabilities.Any()) + { + throw new NullReferenceException("No suitable capability was found."); + } + + foreach (var capability in multipleCapabilities) + { + var driver = BrowserFactory.Create(capability); + ContainerManager.RegisterType(capability.Name, driver); + } + } + + public string CollectEvidence(string destinationPath, string evidenceName) + { + var screenshotPath = string.Empty; + + IEnumerable webDrivers; + using (var scope = ContainerManager.Container.BeginLifetimeScope()) + { + webDrivers = scope.Resolve>(); + } + foreach (var webDriver in webDrivers) + { + try + { + webDriver.SwitchTo().Alert(); + // No screenshot because an Alert is present + } + catch (NoAlertPresentException) + { + var instanceName = ContainerManager.GetNameFromInstance(webDriver); + var ss = ((ITakesScreenshot)webDriver).GetScreenshot(); + ss.SaveAsFile(Path.Combine(destinationPath, $"{evidenceName} - {instanceName}.png")); + } + catch (WebDriverException) { } + + } + + return screenshotPath; + } + + public void Destroy() + { + try + { + IEnumerable webDrivers; + using (var scope = ContainerManager.Container.BeginLifetimeScope()) + { + webDrivers = scope.Resolve>(); + } + + foreach (var webDriver in webDrivers) + { + webDriver.Close(); + webDriver.Dispose(); + } + } + catch (ComponentNotRegisteredException) { } + } + + public string GetEngineName() + { + return _name; + } + + public void Initialize(IEnumerable tags, TestConfiguration testConfiguration) + { + var normalizedTags = tags.Select(x => x.ToUpperInvariant()).ToArray(); + var foundConfiguration = ConfigurationManager.GetValidConfiguration(tags); + + switch (foundConfiguration) + { + case ConfigurationTags.remotedriver: + RegisterSingle(normalizedTags, testConfiguration); + break; + + case ConfigurationTags.multiwebdriver: + RegisterMultiple(normalizedTags, testConfiguration); + break; + } + } + } +} \ No newline at end of file diff --git a/src/Engines/TestWare.Engines.Selenoid/TestWare.Engines.Selenoid.csproj b/src/Engines/TestWare.Engines.Selenoid/TestWare.Engines.Selenoid.csproj new file mode 100644 index 00000000..1c51a306 --- /dev/null +++ b/src/Engines/TestWare.Engines.Selenoid/TestWare.Engines.Selenoid.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + diff --git a/src/Engines/TestWare.Engines.WinAppDriver/Pages/WinAppDriverPage.cs b/src/Engines/TestWare.Engines.WinAppDriver/Pages/WinAppDriverPage.cs index 9462ecac..a126d808 100644 --- a/src/Engines/TestWare.Engines.WinAppDriver/Pages/WinAppDriverPage.cs +++ b/src/Engines/TestWare.Engines.WinAppDriver/Pages/WinAppDriverPage.cs @@ -1,5 +1,5 @@ using TestWare.Engines.Selenium.Pages; -using TestWare.Engines.Appium.Extras; +using TestWare.Engines.Common.Extras; using TestWare.Engines.Appium.WinAppDriver.Factory; namespace TestWare.Engines.Appium.WinAppDriver.Pages; @@ -10,6 +10,6 @@ public abstract class WinAppDriverPage : PageBase protected WinAppDriverPage(IWindowsDriver driver) { Driver = driver; - Selenium.Extras.PageFactory.InitElements(Driver, this); + PageFactory.InitElements(Driver, this); } } diff --git a/src/Engines/TestWare.Engines.WinAppDriver/TestWare.Engines.Appium.WinAppDriver.csproj b/src/Engines/TestWare.Engines.WinAppDriver/TestWare.Engines.Appium.WinAppDriver.csproj index 678a14c9..46b59d91 100644 --- a/src/Engines/TestWare.Engines.WinAppDriver/TestWare.Engines.Appium.WinAppDriver.csproj +++ b/src/Engines/TestWare.Engines.WinAppDriver/TestWare.Engines.Appium.WinAppDriver.csproj @@ -7,7 +7,7 @@ - +