Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add label element #154

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,16 @@ public IButtonElement GetButtonElement(string caption)
var element = _webElementStorageService.Get<IButtonElement>(caption + "Button");
return element;
}

/// <summary>
/// Transforms a step argument into a label element based on the caption.
/// </summary>
/// <param name="caption">The caption of the label element.</param>
/// <returns>The label element matching the caption.</returns>
[StepArgumentTransformation]
public ILabelElement GetLabelElement(string caption)
{
var element = _webElementStorageService.Get<ILabelElement>(caption + "Label");
return element;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Behavioral.Automation.AsyncAbstractions.UI.Interfaces;

public interface ILabelElement : IWebElement
{
public Task ShouldHaveTextAsync(string text);
public Task ShouldNotHaveTextAsync(string text);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Behavioral.Automation.AsyncAbstractions.UI.Interfaces;
using TechTalk.SpecFlow;

namespace Behavioral.Automation.AsyncAbstractions.UI.StepDefinitions;

[Binding]
public class LabelStepDefinitions
{
/// <summary>
/// Check that element's text is equal to the expected one
/// </summary>
/// <param name="label">Tested label object</param>
/// <param name="type">Assertion behavior</param>
/// <param name="value">Expected value</param>
/// <example>Then "Test" element text should be "expected text"</example>
[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();
}
}
Original file line number Diff line number Diff line change
@@ -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<Config>().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"};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Behavioral.Automation.AsyncAbstractions.UI.BasicImplementations;

namespace Behavioral.Automation.Playwright.Selectors;

public class LabelSelector : ElementSelector
{

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@ public WebElementStorageService(WebContext webContext, IObjectContainer objectCo
_objectContainer = objectContainer;
}

//TODO: Impl factory
public T Get<T>(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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Config>().SearchAttribute;

public async Task ShouldBecomeVisibleAsync()
{
await Assertions.Expect(Locator).ToBeVisibleAsync();
Expand All @@ -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)
Copy link
Collaborator

@LukaRukhadze LukaRukhadze Apr 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a good idea to have selector validation here ? Does it mean that we will need to have it in every wrapper method? Isn't it better to have different class and methods for getting Locators, which will check the selector inside them ? E.G. - GetLocator(ElementSelector selector), GetSubLocator(ElementSelector selector, ElementSelector subSelector), GetParentLocator(ElementSelector selector, ElementSelector parentSelector) ?

{
return ((Page) WebContext.Page).GetPlaywrightPage().Locator(ElementSelector.XpathSelector);
}

if (ElementSelector.IdSelector != null)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess automation id selector has more priority than xpath if provided, no?

{
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");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public Configurations(ObjectContainer objectContainer)
public static void ConfigureUiImplementations()
{
IWebElementStorageService.RegisterWebElementImplementationAs<ButtonElement, IButtonElement>();
IWebElementStorageService.RegisterWebElementImplementationAs<LabelElement, ILabelElement>();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Then the "Demo" label should become visible
6 changes: 3 additions & 3 deletions src/BlazorApp/Pages/Counter.razor
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>
<p data-automation-id="count-quantity-label" role="status">Current count: @_currentCount</p>

<button data-automation-id="increment-count-button" class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
private int currentCount = 0;
private int _currentCount = 0;

[Parameter]
public int IncrementAmount { get; set; } = 1;

private void IncrementCount()
{
currentCount += IncrementAmount;
_currentCount += IncrementAmount;
}
}
2 changes: 1 addition & 1 deletion src/BlazorApp/Pages/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ Welcome to your new app.

<SurveyPrompt Title="test" />

<Counter IncrementAmount="10" />
<Counter IncrementAmount="1" />
Loading