This package is a library with core functions simplifying work with Selenium-controlled applications.
You've got to use this set of methods, related to most common actions performed with elements.
Most of performed methods are logged using NLog, so you can easily see a history of performed actions in your log. We support different logging languages.
We use interfaces where is possible, so you can implement your own version of target interface with no need to rewrite other classes.
We use Dependency Injection to simplify overriding of implementations.
-
Applications component provides classes and interfaces which help us work with application and DI container. AqualityServices can get\set service provider and application. Startup is needed to setup DI container.
-
Configurations component provides classes and interfaces which describe most common configurations of project.
-
Elements component describes classes and interfaces which works with UI elements.
-
Solution contains logger and support several languages, Localization and Logging components helps us to implement this.
-
Resources contains localization and project configuration in json files.
-
Utilities.
-
Waitings component contains classes and interfaces which implement some common waitings, for example, wait till condition is satisfied.
-
To start work with this package, simply add the nuget dependency Aquality.Selenium.Core to your project.
-
Setup DI container using Startup.cs.
The simpliest way is to create your AqualityServices class extended from abstract AqualityServices with the following simple signature:
public class AqualityServices : AqualityServices<YourApplication>
{
public new static bool IsApplicationStarted => IsApplicationStarted();
public static YourApplication Application => GetApplication(services => StartApplication(services));
public static IServiceProvider ServiceProvider => GetServiceProvider(services => Application);
private static IApplication StartApplication(IServiceProvider services)
{
your implementation;
}
}
If you need to register your own services / rewrite the implementation, you need override Startup and implement AqualityServices like in example below:
public class AqualityServices : AqualityServices<IApplication>
{
private static ThreadLocal<YourStartup> startup = new ThreadLocal<YourStartup>();
public new static bool IsApplicationStarted => IsApplicationStarted();
public static YourApplication Application => GetApplication(StartApplicationFunction, () => startup.Value.ConfigureServices(new ServiceCollection(), services => Application));
public static IServiceProvider ServiceProvider => GetServiceProvider(services => Application,
() => startup.Value.ConfigureServices(new ServiceCollection(), services => Application));
public static void SetStartup(Startup startup)
{
if (startup != null)
{
AqualityServices.startup.Value = (YourStartup)startup;
}
}
private static Func<IServiceProvider, YourApplication> StartApplicationFunction => (services) => your implementation;
}
public class YourStartup : Startup
{
public override IServiceCollection ConfigureServices(IServiceCollection services, Func<IServiceProvider, IApplication> applicationProvider, ISettingsFile settings = null)
{
var settingsFile = new JsonSettingsFile($"Resources.settings.{SpecialSettingsFile}.json", Assembly.GetExecutingAssembly());
base.ConfigureServices(services, applicationProvider, settingsFile);
//your services like services.AddSingleton<ITimeoutConfiguration>(new TestTimeoutConfiguration(settingsFile));
return services;
}
}
- That's it! Work with Application via AqualityServices or via element services.
All the services could be resolved from the DI container via ServiceProvider.
AqualityServices.Application.Driver.FindElement(CalculatorWindow.OneButton).Click();
AqualityServices.ServiceProvider.GetService<IConditionalWait>().WaitFor(driver =>
{
return driver.FindElements(By.XPath("//*")).Count > 0;
})
AqualityServices.ServiceProvider.GetService<IElementFinder>()
.FindElement(CalculatorWindow.ResultsLabel, timeout: LittleTimeout)
- Extend your elements from Element class:
public abstract class WindowElement : Element
{
protected WindowElement(By locator, string name, ElementState state) : base(locator, name, state)
{
}
protected override IElementActionRetrier ActionRetrier => AqualityServices.ServiceProvider.GetService<IElementActionRetrier>();
protected override IApplication Application => ApplicationManager.Application;
protected override IConditionalWait ConditionalWait => AqualityServices.ServiceProvider.GetService<IConditionalWait>();
protected override IElementFactory Factory => AqualityServices.ServiceProvider.GetService<IElementFactory>();
protected override IElementFinder Finder => AqualityServices.ServiceProvider.GetService<IElementFinder>();
protected override ILocalizedLogger LocalizedLogger => AqualityServices.ServiceProvider.GetService<ILocalizedLogger>();
}
public class Label : WindowElement
{
public Label(By locator, string name, ElementState state) : base(locator, name, state)
{
}
protected override string ElementType => "Label";
}
- Extend ElementFactory to get your own elements:
public static class ElementFactoryExtensions
{
public static Label GetLabel(this IElementFactory elementFactory, By elementLocator, string elementName)
{
return elementFactory.GetCustomElement(GetLabelSupplier(), elementLocator, elementName);
}
private static ElementSupplier<Label> GetLabelSupplier()
{
return (locator, name, state) => new Label(locator, name, state);
}
}
Or create your own ElementFactory! You can extend it from Core's ElementFactory or just implement IElementFactory interface. (Don't forget to register it in the DI container at AqualityServices!).
- Work with Windows/Pages/Forms according to PageObject pattern. Create a base Form class with protected access to IApplication instance and IElementFactory (and any other needed service) via ApplicationManager. Other forms will inherit from this one with the mentioned services available. Take a look at example here:
/// <summary>
/// Defines base class for any UI form.
/// </summary>
public abstract class Form
{
/// <summary>
/// Constructor with parameters.
/// </summary>
/// <param name="locator">Unique locator of the form.</param>
/// <param name="name">Name of the form.</param>
protected Form(By locator, string name)
{
Locator = locator;
Name = name;
}
/// <summary>
/// Locator of specified form.
/// </summary>
public By Locator { get; }
/// <summary>
/// Name of specified form.
/// </summary>
public string Name { get; }
/// <summary>
/// Instance of logger <see cref="Logging.Logger">
/// </summary>
/// <value>Logger instance.</value>
protected Logger Logger => AqualityServices.ServiceProvider.GetService<Logger>();
/// <summary>
/// Element factory <see cref="IElementFactory">
/// </summary>
/// <value>Element factory.</value>
protected IElementFactory ElementFactory => AqualityServices.ServiceProvider.GetService<IElementFactory>();
/// <summary>
/// Return form state for form locator
/// </summary>
/// <value>True - form is opened,
/// False - form is not opened.</value>
public bool IsDisplayed => FormLabel.State.WaitForDisplayed();
/// <summary>
/// Gets size of form element defined by its locator.
/// </summary>
public Size Size => FormLabel.GetElement().Size;
private Label FormLabel => ElementFactory.GetLabel(Locator, Name);
}
If you've got any questions, take a look at Aquality.Selenium.Core.Tests project - probably it already has an implementation of what you're trying to achieve. Also feel free to ask any project's collaborator / to create an issue if needed.
Library's source code is made available under the Apache 2.0 license.