diff --git a/Behavioral.Automation.AsyncAbstractions.UI/BasicImplementations/ElementTransformations.cs b/Behavioral.Automation.AsyncAbstractions.UI/BasicImplementations/ElementTransformations.cs index 7193194..245537f 100644 --- a/Behavioral.Automation.AsyncAbstractions.UI/BasicImplementations/ElementTransformations.cs +++ b/Behavioral.Automation.AsyncAbstractions.UI/BasicImplementations/ElementTransformations.cs @@ -31,4 +31,16 @@ public IButtonElement GetButtonElement(string caption) var element = _webElementStorageService.Get(caption + "Button"); return element; } + + /// + /// Transforms a step argument into a label element based on the caption. + /// + /// The caption of the label element. + /// The label element matching the caption. + [StepArgumentTransformation] + public ILabelElement GetLabelElement(string caption) + { + var element = _webElementStorageService.Get(caption + "Label"); + return element; + } } \ No newline at end of file diff --git a/Behavioral.Automation.AsyncAbstractions.UI/Interfaces/ILabelElement.cs b/Behavioral.Automation.AsyncAbstractions.UI/Interfaces/ILabelElement.cs new file mode 100644 index 0000000..0e27d49 --- /dev/null +++ b/Behavioral.Automation.AsyncAbstractions.UI/Interfaces/ILabelElement.cs @@ -0,0 +1,7 @@ +namespace Behavioral.Automation.AsyncAbstractions.UI.Interfaces; + +public interface ILabelElement : IWebElement +{ + public Task ShouldHaveTextAsync(string text); + public Task ShouldNotHaveTextAsync(string text); +} \ No newline at end of file diff --git a/Behavioral.Automation.AsyncAbstractions.UI/StepDefinitions/LabelStepDefinitions.cs b/Behavioral.Automation.AsyncAbstractions.UI/StepDefinitions/LabelStepDefinitions.cs new file mode 100644 index 0000000..f594878 --- /dev/null +++ b/Behavioral.Automation.AsyncAbstractions.UI/StepDefinitions/LabelStepDefinitions.cs @@ -0,0 +1,35 @@ +using Behavioral.Automation.AsyncAbstractions.UI.Interfaces; +using TechTalk.SpecFlow; + +namespace Behavioral.Automation.AsyncAbstractions.UI.StepDefinitions; + +[Binding] +public class LabelStepDefinitions +{ + /// + /// Check that element's text is equal to the expected one + /// + /// Tested label object + /// Assertion behavior + /// Expected value + /// Then "Test" element text should be "expected text" + [Given("the \"(.*?)\" label text (is|is not) \"(.*)\"")] + [Then("the \"(.*?)\" label text should (become|become not) \"(.*)\"")] + public async Task CheckSelectedText(ILabelElement label, string type, string value) + { + if (type.Equals("is") || type.Equals("become")) + { + await label.ShouldHaveTextAsync(value); + } + else + { + await label.ShouldNotHaveTextAsync(value); + } + } + + [Then(@"the ""(.*)"" label should become visible")] + public async Task CheckLabelVisibility(ILabelElement label) + { + await label.ShouldBecomeVisibleAsync(); + } +} \ No newline at end of file diff --git a/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Pages/MainPage.cs b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Pages/MainPage.cs index 42875b5..1f2ebb4 100644 --- a/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Pages/MainPage.cs +++ b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Pages/MainPage.cs @@ -1,15 +1,10 @@ -using Behavioral.Automation.Configs; -using Behavioral.Automation.Playwright.Configs; using Behavioral.Automation.Playwright.Selectors; -using Behavioral.Automation.Playwright.Services.ElementSelectors; namespace Behavioral.Automation.Playwright.Pages; class MainPageExample : ISelectorStorage { - private static readonly string Id = ConfigManager.GetConfig().SearchAttribute; - public ButtonSelector IncrementCountButton = new() {XpathSelector = "//button[@data-automation-id='increment-count-button']"}; - - public ElementSelector DemoLabel = new() {IdSelector = "label-simple-text"}; + public LabelSelector DemoLabel = new() {IdSelector = "label-simple-text"}; + public LabelSelector CountQuantityLabel = new LabelSelector() {IdSelector = "count-quantity-label"}; } \ No newline at end of file diff --git a/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Selectors/LabelSelector.cs b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Selectors/LabelSelector.cs new file mode 100644 index 0000000..641f2b5 --- /dev/null +++ b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Selectors/LabelSelector.cs @@ -0,0 +1,8 @@ +using Behavioral.Automation.AsyncAbstractions.UI.BasicImplementations; + +namespace Behavioral.Automation.Playwright.Selectors; + +public class LabelSelector : ElementSelector +{ + +} \ No newline at end of file diff --git a/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Services/WebElementStorageService.cs b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Services/WebElementStorageService.cs index 8381da8..0b34968 100644 --- a/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Services/WebElementStorageService.cs +++ b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/Services/WebElementStorageService.cs @@ -20,15 +20,18 @@ public WebElementStorageService(WebContext webContext, IObjectContainer objectCo _objectContainer = objectContainer; } - //TODO: Impl factory public T Get(string elementName) where T : IWebElement { var pages = GetAllPagesWithElements(); var elementSelector = GetElementSelector(pages, elementName); // Select proper realisation for element according to registered class in DI framework: + // TODO: add validation. Throw meaningful error if realization is not registered var classType = IWebElementStorageService.RegisteredElements[typeof(T)]; - var element = (IWebElement) Activator.CreateInstance(classType, _webContext, elementSelector); + var referenceToANewWebElement = Activator.CreateInstance(classType, _webContext, elementSelector); + // TODO: Add more meaningful exception message: + if (referenceToANewWebElement == null) throw new Exception("Can't create instance of a Web Element"); + var element = (IWebElement) referenceToANewWebElement; element.Description = elementName; return (T) element; } diff --git a/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/WebElementsWrappers/LabelElement.cs b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/WebElementsWrappers/LabelElement.cs new file mode 100644 index 0000000..f9929f8 --- /dev/null +++ b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/WebElementsWrappers/LabelElement.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using Behavioral.Automation.AsyncAbstractions.UI.BasicImplementations; +using Behavioral.Automation.AsyncAbstractions.UI.Interfaces; +using Behavioral.Automation.Playwright.Selectors; +using Microsoft.Playwright; + +namespace Behavioral.Automation.Playwright.WebElementsWrappers; + +public class LabelElement : PlaywrightWebElement, ILabelElement +{ + public LabelElement(WebContext webContext, LabelSelector selector) : base(webContext, selector) + { + } + + public async Task ShouldHaveTextAsync(string text) + { + await Assertions.Expect(Locator).ToHaveTextAsync(text); + } + + public async Task ShouldNotHaveTextAsync(string text) + { + await Assertions.Expect(Locator).Not.ToHaveTextAsync(text); + } +} \ No newline at end of file diff --git a/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/WebElementsWrappers/PlayWrightWebElement.cs b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/WebElementsWrappers/PlayWrightWebElement.cs index 1d567ae..b74d894 100644 --- a/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/WebElementsWrappers/PlayWrightWebElement.cs +++ b/Behavioral.Automation.Playwright/Behavioral.Automation.Playwright/WebElementsWrappers/PlayWrightWebElement.cs @@ -2,6 +2,8 @@ using System.Threading.Tasks; using Behavioral.Automation.AsyncAbstractions.UI.BasicImplementations; using Behavioral.Automation.AsyncAbstractions.UI.Interfaces; +using Behavioral.Automation.Configs; +using Behavioral.Automation.Playwright.Configs; using Microsoft.Playwright; namespace Behavioral.Automation.Playwright.WebElementsWrappers; @@ -11,6 +13,8 @@ public abstract class PlaywrightWebElement : IWebElement public WebContext WebContext { get; } public ElementSelector ElementSelector { get; } public string? Description { get; set; } + private static readonly string Id = ConfigManager.GetConfig().SearchAttribute; + public async Task ShouldBecomeVisibleAsync() { await Assertions.Expect(Locator).ToBeVisibleAsync(); @@ -28,10 +32,18 @@ public ILocator Locator { if (WebContext is null) throw new NullReferenceException("Please set web context."); // Locator for Playwright can be retrieved from Page element: - var selector = (ElementSelector.XpathSelector != null) - ? ElementSelector.XpathSelector - : ElementSelector.IdSelector; - return ((Page) WebContext.Page).GetPlaywrightPage().Locator(selector); + if (ElementSelector.XpathSelector != null) + { + return ((Page) WebContext.Page).GetPlaywrightPage().Locator(ElementSelector.XpathSelector); + } + + if (ElementSelector.IdSelector != null) + { + return ((Page) WebContext.Page).GetPlaywrightPage().Locator($"//*[@{Id}='{ElementSelector.IdSelector}']"); + } + + // TODO: Think about moving validation into element factory method or in element constructor + throw new Exception("Please provide XpathSelector or IdSelector"); } } } \ No newline at end of file diff --git a/Behavioral.Automation.Playwright/UITests/Configurations/Configurations.cs b/Behavioral.Automation.Playwright/UITests/Configurations/Configurations.cs index 302472c..5fd9574 100644 --- a/Behavioral.Automation.Playwright/UITests/Configurations/Configurations.cs +++ b/Behavioral.Automation.Playwright/UITests/Configurations/Configurations.cs @@ -23,6 +23,7 @@ public Configurations(ObjectContainer objectContainer) public static void ConfigureUiImplementations() { IWebElementStorageService.RegisterWebElementImplementationAs(); + IWebElementStorageService.RegisterWebElementImplementationAs(); } /// diff --git a/Behavioral.Automation.Playwright/UITests/Features/Tests.feature b/Behavioral.Automation.Playwright/UITests/Features/Tests.feature index c30cdc8..1e65d2a 100644 --- a/Behavioral.Automation.Playwright/UITests/Features/Tests.feature +++ b/Behavioral.Automation.Playwright/UITests/Features/Tests.feature @@ -2,8 +2,10 @@ Scenario: Check button click Given application URL is opened + And the "Count quantity" label text is "Current count: 0" When user clicks the "Increment count" button + Then the "Count quantity" label text should become "Current count: 1" Scenario: Visibility binding check Given application URL is opened - Then the "Demo label" should be visible \ No newline at end of file + Then the "Demo" label should become visible \ No newline at end of file diff --git a/src/BlazorApp/Pages/Counter.razor b/src/BlazorApp/Pages/Counter.razor index acf2173..b6746a7 100644 --- a/src/BlazorApp/Pages/Counter.razor +++ b/src/BlazorApp/Pages/Counter.razor @@ -4,18 +4,18 @@

Counter

-

Current count: @currentCount

+

Current count: @_currentCount

@code { - private int currentCount = 0; + private int _currentCount = 0; [Parameter] public int IncrementAmount { get; set; } = 1; private void IncrementCount() { - currentCount += IncrementAmount; + _currentCount += IncrementAmount; } } diff --git a/src/BlazorApp/Pages/Index.razor b/src/BlazorApp/Pages/Index.razor index 03a70e8..d7d3285 100644 --- a/src/BlazorApp/Pages/Index.razor +++ b/src/BlazorApp/Pages/Index.razor @@ -10,4 +10,4 @@ Welcome to your new app. - +