Getting Started

Atata Framework - C#/.NET web test automation full-featured framework based on Selenium WebDriver. It uses a fluent page object pattern; has a built-in logging system; contains a unique triggers functionality; has a set of ready-to-use components. One of the key ideas of the framework is to provide a simple and intuitive syntax for defining and using page objects. A page object implementation requires as less code as possible. You can describe a page object class without any methods and only have a set of properties marked with attributes representing page components.

The framework is completely open source and hosted on GitHub as Atata Framework organization under the Apache License 2.0. All framework libraries are published and available as NuGet packages.

The framework basically consists of the following concepts:

  • AtataContext
    • Configuration - use fluent builder and/or JSON config.
  • Components
    • Controls
    • Page objects
    • Control list
  • Attributes
    • Attributes of control search - basically, element locators, like [FindById], [FindByName], [FindByXPath], etc.
    • Trigger attributes - a functionality that is automatically executed in response to certain events on a particular component. For example, when a click on button occurs it may be defined that a wait should be executed.
    • Behavior attributes - change the way how particular actions are executed. For example, specify the click behavior for a specific button by adding [ClickUsingScript] to perform the click using JavaScript, instead of default IWebElement.Click() way.
    • Settings attributes - set settings for control finding, culture, value format, etc.
  • Verification functionality
    • .Should.* - an assertion. For example: UserName.Should.Be("John").
    • .ExpectTo.* - an expectation, which produces a warning.
    • .WaitTo.* - a waiting for a certain condition.

Atata Framework consists of a set of NuGet packages.

Main Packages

  • Atata (GitHub | NuGet) - is a main package, which provides base and main functionality for web UI testing.
  • Atata.Configuration.Json (GitHub | NuGet) - provides configuration through JSON files.
  • Atata.WebDriverSetup (GitHub | NuGet) - sets up browser drivers, e.g. chromedriver, geckodriver, etc. Basically, it provides functionality similar to Java WebDriverManager.
  • Atata.HtmlValidation (GitHub | NuGet) - adds HTML page validation to Atata using html-validate NPM package.
  • Atata.Bootstrap (GitHub | NuGet) - contains a set of Atata components for Bootstrap Framework.
  • Atata.KendoUI (GitHub | NuGet) - contains a set of Atata components for Kendo UI HTML Framework.

Additional Packages

  • Atata.WebDriverExtras (GitHub | NuGet) - contains extension methods and other extra classes for Selenium WebDriver. Used by Atata.
  • Atata.Cli (GitHub | NuGet) - provides an API for CLI.
  • Atata.Cli.Npm (GitHub | NuGet) - provides an API for CLI of NPM.
  • Atata.Cli.HtmlValidate (GitHub | NuGet) - provides an API for CLI of html-validate NPM package.

There are 2 options to create a project in Visual Studio for automated testing using Atata Framework.

Install via Atata Templates

To get started, install Atata Templates Visual Studio extension.

The extension provides the following templates:

  • Project templates:
    • Atata NUnit Basic Test Project (.NET 6)
    • Atata NUnit Basic Test Project (.NET 7)
    • Atata NUnit Advanced Test Project (.NET 6)
    • Atata NUnit Advanced Test Project (.NET 7)
  • Item templates:
    • Atata Page Object
    • Atata Base Page Object
    • Atata Control
    • Atata Trigger
    • Atata NUnit Test Fixture
    • Atata NUnit Base Test Fixture

Create Project

When extension is installed, you can create a project of one of Atata project types. In Visual Studio:

  1. Go to File/New/Project… or File/Add/New Project… (to add to existing solution)
  2. Type Atata into search box or choose Atata in “project types” drop-down
  3. Choose template (e.g.: Atata NUnit Advanced Test Project (.NET 6)) and specify project name and location

Atata Templates project

Project References

The project is created with NuGet package references:

Configuration

You don’t need to configure specific browser driver packages, as a project is by default configured to automatically download appropriate driver, by use of Atata.WebDriverSetup package.

In the created project you can specify your testing site base URL and appropriate driver in SetUpFixture.cs or Atata.local.json class, depending on type of project (basic or advanced).

AtataContext.GlobalConfiguration
    .UseChrome()
        .WithArguments("start-maximized")
    .UseBaseUrl("https://atata.io/")
    //...
{
  "baseUrl": "https://atata.io/"
  // Other environment specific configuration properties can be added here.
}

Just replace "https://atata.io/" string with your URL.

Test Fixtures

The created project also contains ready to use SampleTests.cs fixture, which can be either modified or removed:

namespace AtataUITests1;

public class SampleTests : UITestFixture
{
    [Test]
    public void SampleTest() =>
        Go.To<OrdinaryPage>()
            .PageTitle.Should.Contain("Atata");
}

Further test fixture classes are recommended to inherit from UITestFixture, or just choose “Atata NUnit Test Fixture” item template in “Add New Item Window”.

Create Project Targeting Other .NET Version

  1. Create a project using one of the project templates: “Atata NUnit Basic Test Project (.NET 6)”, “Atata NUnit Advanced Test Project (.NET 6)”.
  2. Open project .csproj file.
  3. Change the value of <TargetFramework> tag from net6.0 to the needed version.
    <Project Sdk="Microsoft.NET.Sdk">
       
      <PropertyGroup>
        <TargetFramework>NET_VERSION</TargetFramework>
        <!-- ... -->
      </PropertyGroup>
       
      <!-- ... -->
       
    </Project>
    

Install via NuGet

It is a more custom approach to create Atata testing project. To get started just add Atata NuGet package to the project of Class Library (.NET Framework, .NET Core or .NET 5+) type in Visual Studio.

PM> Install-Package Atata

The Atata package depends on the following packages, which are added automatically:

You might also need to install Atata.WebDriverSetup package for auto-setup of browser drivers, e.g. chromedriver, geckodriver, etc. This is a recommended option. Alternatively, you can install a driver package for specific browser: Selenium.WebDriver.ChromeDriver, Selenium.WebDriver.IEDriver, etc. But be aware to keep version synchronization of browser and driver.

You are also free to select any test engine framework: NUnit, Xunit, MSTest, SpecFlow, etc.

For .NET Core and .NET 5+ projects it is required also to add Microsoft.NET.Test.Sdk package to the project that contains tests (no matter NUnit, xUnit, MSTest, etc.).

NUnit Project Configuration

In addition to Atata NuGet package, add the following packages:

Then add 2 C# class files.

The first SetUpFixture class sets the global Atata configuration, like which browser driver to use, basic URL, etc. Then it sets up an appropriate driver executable for the browser to use.

SetUpFixture.cs

using Atata;
using NUnit.Framework;

namespace SampleApp.UITests
{
    [SetUpFixture]
    public class SetUpFixture
    {
        [OneTimeSetUp]
        public void GlobalSetUp()
        {
            AtataContext.GlobalConfiguration
                .UseChrome()
                    .WithArguments("start-maximized")
                .UseBaseUrl("https://atata.io/")
                .UseCulture("en-US")
                .UseAllNUnitFeatures();

            AtataContext.GlobalConfiguration.AutoSetUpDriverToUse();
        }
    }
}

The second UITestFixture class is the base class for all test fixture classes that have UI tests. It just starts an Atata session before the test run and closes it after the test is finished.

SetUpFixture.cs

using Atata;
using NUnit.Framework;

namespace SampleApp.UITests
{
    [TestFixture]
    [Parallelizable(ParallelScope.Self)]
    public class UITestFixture
    {
        [SetUp]
        public void SetUp()
        {
            AtataContext.Configure().Build();
        }

        [TearDown]
        public void TearDown()
        {
            AtataContext.Current?.CleanUp();
        }
    }
}

Xunit Project Configuration

Check out Xunit Atata sample project.

MSTest Project Configuration

Check out MSTest Atata sample project.

SpecFlow Project Configuration

Check out SpecFlow Atata sample project.

Possible Issues

WebDriver .NET Performance Issue

If your Atata testing actions go very slowly, check out this issue.

There is a rare issue using Selenium WebDriver since v3.6.0 for .NET Core 2.0+: each WebDriver request takes extra 1 second. It makes execution of WebDriver actions very slow. The fix is added within Atata package. Use WithFixOfCommandExecutionDelay driver configurational method to get around of this bug.

AtataContext.GlobalConfiguration
    .UseChrome()
        .WithFixOfCommandExecutionDelay();

It is equivalent to:

AtataContext.GlobalConfiguration
    .UseChrome()
        .WithHostName("127.0.0.1");

Using Atata.Configuration.Json:

{
  "driver": {
    "type": "chrome",
    "service": {
      "hostName": "127.0.0.1"
    }
  }
}

Check the fix details: #101 Fix command execution delay of WebDriver for .NET Core 2.0.

Lat’s create a simple test automation project example with a test for Sign In page.

Create Project

In Visual Studio create a project using Installation instructions.

Set Atata base URL to "https://demo.atata.io/" either in SetUpFixture.cs or Atata.local.json file.

Define Page Object Class

SignInPage.cs

using Atata;

namespace AtataDemo.UITests
{
    using _ = SignInPage;

    [Url("signin")]
    [VerifyH1]
    public class SignInPage : Page<_>
    {
        public TextInput<_> Email { get; private set; }

        public PasswordInput<_> Password { get; private set; }

        public Button<_> SignIn { get; private set; }
    }
}

SignInPage is a page object class that is marked with a few attributes:

  • [Url("signin")] - sets the relative URL of the page to be navigated to.
  • [VerifyH1] - upon page initialization verifies that the page contains <h1> HTML element with “Sign In” text.

Default element search of Email and Password controls is performed by label. Default search of SignIn button is performed by its text.

Implement Test

SignInTests.cs

using Atata;
using NUnit.Framework;

namespace AtataDemo.UITests
{
    [TestFixture]
    public class SignInTests : UITestFixture
    {
        [Test]
        public void SignIn()
        {
            Go.To<SignInPage>()
                .Email.Set("admin@mail.com")
                .Password.Set("abc123")
                .SignIn.Click();
        }
    }
}

View Log

The above sample SignIn test generates the following log to NUnit output:

2023-01-17 17:07:06.9954  INFO Starting test: AtataDemo.UITests.SignInTests.SignIn
2023-01-17 17:07:07.0051 TRACE > Set up AtataContext
2023-01-17 17:07:07.0058 TRACE - Set: BaseUrl=https://demo.atata.io/
2023-01-17 17:07:07.0064 TRACE - Set: ElementFindTimeout=5s; ElementFindRetryInterval=0.5s
2023-01-17 17:07:07.0066 TRACE - Set: WaitingTimeout=5s; WaitingRetryInterval=0.5s
2023-01-17 17:07:07.0066 TRACE - Set: VerificationTimeout=5s; VerificationRetryInterval=0.5s
2023-01-17 17:07:07.0069 TRACE - Set: Culture=en-US
2023-01-17 17:07:07.0071 TRACE - Set: Artifacts=D:\dev\AtataDemo.UITests\AtataDemo.UITests\bin\Debug\netcoreapp3.1\artifacts\20230117T170706\SignInTests\SignIn
2023-01-17 17:07:07.0123 TRACE - Set: DriverService=ChromeDriverService on port 60375
2023-01-17 17:07:07.6802 TRACE - Set: Driver=ChromeDriver (alias=chrome)
2023-01-17 17:07:07.6815 TRACE < Set up AtataContext (0.676s)
2023-01-17 17:07:07.7095  INFO > Go to "Sign In" page by URL https://demo.atata.io/signin
2023-01-17 17:07:07.7100 TRACE - > Navigate to URL https://demo.atata.io/signin
2023-01-17 17:07:07.9629 TRACE - < Navigate to URL https://demo.atata.io/signin (0.252s)
2023-01-17 17:07:07.9820  INFO < Go to "Sign In" page by URL https://demo.atata.io/signin (0.272s)
2023-01-17 17:07:07.9908 TRACE > Execute trigger VerifyH1Attribute { Index=-1, Case=Title, Match=Equals, Timeout=5, RetryInterval=0.5 } on Init against "Sign In" page
2023-01-17 17:07:08.0004  INFO - > Assert: "Sign In" <h1> heading should be present
2023-01-17 17:07:08.0181 TRACE - - > Find visible element by XPath ".//h1[normalize-space(.) = 'Sign In']" in ChromeDriver
2023-01-17 17:07:08.5516 TRACE - - < Find visible element by XPath ".//h1[normalize-space(.) = 'Sign In']" in ChromeDriver (0.532s) >> Element { Id=29914dc5-4805-4fc2-b5d2-d64e43923008 }
2023-01-17 17:07:08.5527  INFO - < Assert: "Sign In" <h1> heading should be present (0.552s)
2023-01-17 17:07:08.5529 TRACE < Execute trigger VerifyH1Attribute { Index=-1, Case=Title, Match=Equals, Timeout=5, RetryInterval=0.5 } on Init against "Sign In" page (0.562s)
2023-01-17 17:07:08.5572  INFO > Set "admin@mail.com" to "Email" text input
2023-01-17 17:07:08.5587 TRACE - > Execute behavior SetsValueUsingClearAndTypeBehaviorsAttribute against "Email" text input
2023-01-17 17:07:08.5597 TRACE - - > Execute behavior ClearsValueUsingClearMethodAttribute against "Email" text input
2023-01-17 17:07:08.5626 TRACE - - - > Find element by XPath ".//label[normalize-space(.) = 'Email']" in ChromeDriver
2023-01-17 17:07:08.5711 TRACE - - - < Find element by XPath ".//label[normalize-space(.) = 'Email']" in ChromeDriver (0.008s) >> Element { Id=0f551d11-0171-4ed1-ba9f-74675df8e2b4 }
2023-01-17 17:07:08.5815 TRACE - - - > Find element by XPath ".//*[normalize-space(@id) = 'email']/descendant-or-self::input[@type='text' or not(@type)]" in ChromeDriver
2023-01-17 17:07:08.5887 TRACE - - - < Find element by XPath ".//*[normalize-space(@id) = 'email']/descendant-or-self::input[@type='text' or not(@type)]" in ChromeDriver (0.007s) >> Element { Id=a54e919d-11b7-484b-b3f9-6f6822e22c00 }
2023-01-17 17:07:08.5906 TRACE - - - > Clear element { Id=a54e919d-11b7-484b-b3f9-6f6822e22c00 }
2023-01-17 17:07:08.6099 TRACE - - - < Clear element { Id=a54e919d-11b7-484b-b3f9-6f6822e22c00 } (0.019s)
2023-01-17 17:07:08.6102 TRACE - - < Execute behavior ClearsValueUsingClearMethodAttribute against "Email" text input (0.050s)
2023-01-17 17:07:08.6107 TRACE - - > Execute behavior TypesTextUsingSendKeysAttribute against "Email" text input
2023-01-17 17:07:08.6118 TRACE - - - > Send keys "admin@mail.com" to element { Id=a54e919d-11b7-484b-b3f9-6f6822e22c00 }
2023-01-17 17:07:08.6584 TRACE - - - < Send keys "admin@mail.com" to element { Id=a54e919d-11b7-484b-b3f9-6f6822e22c00 } (0.046s)
2023-01-17 17:07:08.6586 TRACE - - < Execute behavior TypesTextUsingSendKeysAttribute against "Email" text input (0.047s)
2023-01-17 17:07:08.6587 TRACE - < Execute behavior SetsValueUsingClearAndTypeBehaviorsAttribute against "Email" text input (0.100s)
2023-01-17 17:07:08.6588  INFO < Set "admin@mail.com" to "Email" text input (0.101s)
2023-01-17 17:07:08.6589  INFO > Set "abc123" to "Password" password input
2023-01-17 17:07:08.6591 TRACE - > Execute behavior SetsValueUsingClearAndTypeBehaviorsAttribute against "Password" password input
2023-01-17 17:07:08.6592 TRACE - - > Execute behavior ClearsValueUsingClearMethodAttribute against "Password" password input
2023-01-17 17:07:08.6597 TRACE - - - > Find element by XPath ".//label[normalize-space(.) = 'Password']" in ChromeDriver
2023-01-17 17:07:08.6675 TRACE - - - < Find element by XPath ".//label[normalize-space(.) = 'Password']" in ChromeDriver (0.007s) >> Element { Id=13c05021-8bf3-478a-9882-7ee67751ec67 }
2023-01-17 17:07:08.6761 TRACE - - - > Find element by XPath ".//*[normalize-space(@id) = 'password']/descendant-or-self::input[@type='password']" in ChromeDriver
2023-01-17 17:07:08.6841 TRACE - - - < Find element by XPath ".//*[normalize-space(@id) = 'password']/descendant-or-self::input[@type='password']" in ChromeDriver (0.007s) >> Element { Id=197506e3-8870-45d6-a56c-f6e655b98bc3 }
2023-01-17 17:07:08.6846 TRACE - - - > Clear element { Id=197506e3-8870-45d6-a56c-f6e655b98bc3 }
2023-01-17 17:07:08.7047 TRACE - - - < Clear element { Id=197506e3-8870-45d6-a56c-f6e655b98bc3 } (0.020s)
2023-01-17 17:07:08.7050 TRACE - - < Execute behavior ClearsValueUsingClearMethodAttribute against "Password" password input (0.045s)
2023-01-17 17:07:08.7052 TRACE - - > Execute behavior TypesTextUsingSendKeysAttribute against "Password" password input
2023-01-17 17:07:08.7053 TRACE - - - > Send keys "abc123" to element { Id=197506e3-8870-45d6-a56c-f6e655b98bc3 }
2023-01-17 17:07:08.7382 TRACE - - - < Send keys "abc123" to element { Id=197506e3-8870-45d6-a56c-f6e655b98bc3 } (0.032s)
2023-01-17 17:07:08.7385 TRACE - - < Execute behavior TypesTextUsingSendKeysAttribute against "Password" password input (0.033s)
2023-01-17 17:07:08.7386 TRACE - < Execute behavior SetsValueUsingClearAndTypeBehaviorsAttribute against "Password" password input (0.079s)
2023-01-17 17:07:08.7386  INFO < Set "abc123" to "Password" password input (0.079s)
2023-01-17 17:07:08.7390  INFO > Click "Sign In" button
2023-01-17 17:07:08.7396 TRACE - > Execute behavior ClicksUsingClickMethodAttribute against "Sign In" button
2023-01-17 17:07:08.7408 TRACE - - > Find element by XPath ".//*[self::input[@type='button' or @type='submit' or @type='reset'] or self::button][normalize-space(.) = 'Sign In' or normalize-space(@value) = 'Sign In']" in ChromeDriver
2023-01-17 17:07:08.7497 TRACE - - < Find element by XPath ".//*[self::input[@type='button' or @type='submit' or @type='reset'] or self::button][normalize-space(.) = 'Sign In' or normalize-space(@value) = 'Sign In']" in ChromeDriver (0.008s) >> Element { Id=85cbafb1-d8af-4765-b4c0-2bd0e9a820f3 }
2023-01-17 17:07:08.7502 TRACE - - > Click element { Id=85cbafb1-d8af-4765-b4c0-2bd0e9a820f3 }
2023-01-17 17:07:08.7900 TRACE - - < Click element { Id=85cbafb1-d8af-4765-b4c0-2bd0e9a820f3 } (0.039s)
2023-01-17 17:07:08.7903 TRACE - < Execute behavior ClicksUsingClickMethodAttribute against "Sign In" button (0.050s)
2023-01-17 17:07:08.7904  INFO < Click "Sign In" button (0.051s)
2023-01-17 17:07:08.7915 TRACE > Clean up AtataContext
2023-01-17 17:07:08.9005 TRACE < Clean up AtataContext (0.108s)
2023-01-17 17:07:08.9010  INFO Finished test (1.971s)
2023-01-17 17:07:08.9013  INFO Pure test execution time: 1.109s

Demo atata-framework/atata-sample-app-tests UI tests application demonstrates different testing approaches and features of Atata Framework. It uses Atata Sample App (repository) as a testing website and NUnit 3 as a test engine.

Features

  • Atata configuration and settings set-up.
  • Page navigation.
  • Controls finding.
  • Data input and verification.
  • Validation messages verification.
  • Usage of triggers.
  • Interaction with pop-ups (Bootstrap modal) and alerts.
  • Work with tables.
  • Logging, screenshots and snapshots.
  • Page HTML validation.

Sample Test

public class UserTests : UITestFixture
{
    [Test]
    public void Create() =>
        Login()
            .New()
                .ModalTitle.Should.Be("New User")
                .General.FirstName.SetRandom(out string firstName)
                .General.LastName.SetRandom(out string lastName)
                .General.Email.SetRandom(out string email)
                .General.Office.SetRandom(out Office office)
                .General.Gender.SetRandom(out Gender gender)
                .Save()
            .GetUserRow(email).View()
                .AggregateAssert(x => x
                    .Header.Should.Be($"{firstName} {lastName}")
                    .Email.Should.Be(email)
                    .Office.Should.Be(office)
                    .Gender.Should.Be(gender)
                    .Birthday.Should.Not.BeVisible()
                    .Notes.Should.Not.BeVisible());

    //...
}

In SetUp method just invoke AtataContext.Configure() method that returns AtataContextBuilder instance, then invoke configuration methods and finally invoke Build() method:

[SetUp]
public void SetUp()
{
    AtataContext.Configure()
        // TODO: Use configuration methods.
        .Build();
}

To clean up the AtataContext do the following in the TearDown method:

[TearDown]
public void TearDown()
{
    AtataContext.Current?.CleanUp();
}

It also closes web driver instance as well as a browser.

Also it is recommended to extract a global setup/configuration to global setup method:

[SetUpFixture]
public class SetUpFixture
{
    [OneTimeSetUp]
    public void GlobalSetUp()
    {
        AtataContext.GlobalConfiguration
            // TODO: Use configuration methods.
            .AutoSetUpDriverToUse();
    }
}

Consider taking a look at Atata.Configuration.Json for configuration through JSON files.

Driver

The list of driver configuration methods of AtataContextBuilder:

public ChromeAtataContextBuilder

UseChrome()

Creates and returns a new builder for ChromeDriver with default DriverAliases.Chrome alias. Sets this builder as a one to use for a driver creation.

public FirefoxAtataContextBuilder

UseFirefox()

Creates and returns a new builder for FirefoxDriver with default DriverAliases.Firefox alias. Sets this builder as a one to use for a driver creation.

public InternetExplorerAtataContextBuilder

UseInternetExplorer()

Creates and returns a new builder for InternetExplorerDriver with default DriverAliases.InternetExplorer alias. Sets this builder as a one to use for a driver creation.

public EdgeAtataContextBuilder

UseEdge()

Creates and returns a new builder for EdgeDriver with default DriverAliases.Edge alias. Sets this builder as a one to use for a driver creation.

public SafariAtataContextBuilder

UseSafari()

Creates and returns a new builder for SafariDriver with default DriverAliases.Safari alias. Sets this builder as a one to use for a driver creation.

public RemoteDriverAtataContextBuilder

UseRemoteDriver()

Creates and returns a new builder for RemoteWebDriver with default DriverAliases.Remote alias. Sets this builder as a one to use for a driver creation.

public CustomDriverAtataContextBuilder

UseDriver(IWebDriver driver)

Use the specified driver instance.

public CustomDriverAtataContextBuilder

UseDriver(Func<IWebDriver> driverFactory)

Use the custom driver factory method.

public TDriverBuilder

UseDriver<TDriverBuilder>(TDriverBuilder driverBuilder)

Use the driver builder.

public AtataContextBuilder

UseDriver(string alias)

Sets the alias of the driver to use.

public ChromeAtataContextBuilder

ConfigureChrome(string alias = DriverAliases.Chrome)

Returns an existing or creates a new builder for ChromeDriver by the specified alias.

public FirefoxAtataContextBuilder

ConfigureFirefox(string alias = DriverAliases.Firefox)

Returns an existing or creates a new builder for FirefoxDriver by the specified alias.

public InternetExplorerAtataContextBuilder

ConfigureInternetExplorer(string alias = DriverAliases.InternetExplorer)

Returns an existing or creates a new builder for InternetExplorerDriver by the specified alias.

public EdgeAtataContextBuilder

ConfigureEdge(string alias = DriverAliases.Edge)

Returns an existing or creates a new builder for EdgeDriver by the specified alias.

public SafariAtataContextBuilder

ConfigureSafari(string alias = DriverAliases.Safari)

Returns an existing or creates a new builder for SafariDriver by the specified alias.

public RemoteDriverAtataContextBuilder

ConfigureRemoteDriver(string alias = DriverAliases.Remote)

Returns an existing or creates a new builder for RemoteWebDriver by the specified alias.

public TDriverBuilder

ConfigureDriver<TDriverBuilder>(string alias, Func<TDriverBuilder> driverBuilderCreator)

Returns an existing or creates a new builder for TDriverBuilder by the specified alias.

public AtataContextBuilder

UseDriverInitializationStage(AtataContextDriverInitializationStage stage)

Sets the driver initialization stage. The default value is AtataContextDriverInitializationStage.Build.

public void

AutoSetUpDriverToUse()

public async Task

AutoSetUpDriverToUseAsync()

Sets up driver with auto version detection for the local browser to use. Gets the name of the local browser to use from AtataBuildingContext.LocalBrowserToUseName property. Then invokes Atata.WebDriverSetup.DriverSetup.AutoSetUpSafely(...) static method from Atata.WebDriverSetup package.

public void

AutoSetUpConfiguredDrivers()

public async Task

AutoSetUpConfiguredDriversAsync()

Sets up drivers with auto version detection for the local configured browsers. Gets the names of configured local browsers from AtataBuildingContext.ConfiguredLocalBrowserNames property. Then invokes Atata.WebDriverSetup.DriverSetup.AutoSetUpSafely(...) static method from Atata.WebDriverSetup package.

Driver Configuration

It is possible to configure the driver using the following methods of driver builders. The amount of methods vary dependently of the driver builder type, for example Chrome and Edge builders support the most amount.

public {DriverAtataContextBuilder}

WithArguments(params string[] arguments)

public {DriverAtataContextBuilder}

WithArguments(IEnumerable<string> arguments)

Adds arguments to be appended to the browser executable command line.

public {DriverAtataContextBuilder}

WithAlias(string alias)

Specifies the driver alias.

public {DriverAtataContextBuilder}

WithDownloadDirectory(string directoryPath)

Adds the download.default_directory user profile preference to options with the value specified by directoryPath.

public {DriverAtataContextBuilder}

WithDownloadDirectory(Func<string> directoryPathBuilder)

Adds the download.default_directory user profile preference to options with the value specified by directoryPathBuilder.

public {DriverAtataContextBuilder}

WithArtifactsAsDownloadDirectory()

Adds the download.default_directory user profile preference to options with the value of Artifacts directory path.

public {DriverAtataContextBuilder}

WithOptions{DriverOptions} options)

Specifies the driver options.

public {DriverAtataContextBuilder}

WithOptions(Func<{DriverOptions}> optionsCreator)

Specifies the driver options factory method.

public {DriverAtataContextBuilder}

WithOptions(Action<{DriverOptions}> optionsInitializer)

Specifies the driver options initialization method.

public {DriverAtataContextBuilder}

WithOptions(Dictionary<string, object> optionsPropertiesMap)

Specifies the properties map for the driver options.

public {DriverAtataContextBuilder}

AddAddionalOption(string optionName, object optionValue)

Adds the additional option to the driver options.

public {DriverAtataContextBuilder}

AddAdditionalBrowserOption(string optionName, object optionValue)

Adds the additional browser option to the driver options.

public {DriverAtataContextBuilder}

WithDriverService(Func<{DriverService}> driverServiceCreator)

Specifies the driver service factory method.

public {DriverAtataContextBuilder}

WithDriverService(Action<{DriverService}> serviceInitializer)

Specifies the driver service initialization method.

public {DriverAtataContextBuilder}

WithDriverService(Dictionary<string, object> servicePropertiesMap)

Specifies the properties map for the driver service.

public {DriverAtataContextBuilder}

WithDriverPath(string driverPath)

Specifies the directory containing the driver executable file.

public {DriverAtataContextBuilder}

WithLocalDriverPath()

Specifies that local/current directory should be used as the directory containing the driver executable file. Uses AppDomain.CurrentDomain.BaseDirectory as driver folder path. This configuration option makes sense for .NET Core 2.0+ project that uses driver as a project package (hosted in the same build directory).

public {DriverAtataContextBuilder}

WithDriverExecutableFileName(string driverExecutableFileName)

Specifies the name of the driver executable file.

public {DriverAtataContextBuilder}

WithCommandTimeout(TimeSpan commandTimeout)

Specifies the command timeout (the maximum amount of time to wait for each command). The default timeout is 60 seconds.

public {DriverAtataContextBuilder}

WithHostName(string hostName)

Specifies the host name of the service. The default value is localhost. This configuration option makes sense for .NET Core 2.0 to be set to 127.0.0.1 for IPv4 and [::1] for IPv6. There is a bug (https://github.com/dotnet/corefx/issues/24104) in .NET Core 2.0: each WebDriver request takes extra 1 second.

public {DriverAtataContextBuilder}

WithFixOfCommandExecutionDelay()

Specifies that the fix of driver’s HTTP command execution delay should be applied. Invokes WithHostName("127.0.0.1") method. This configuration option makes sense for .NET Core 2.0 that works within IPv4. There is a bug (https://github.com/dotnet/corefx/issues/24104) in .NET Core 2.0: each WebDriver request takes extra 1 second.

public {DriverAtataContextBuilder}

WithPortsToIgnore(params int[] portsToIgnore)

public {DriverAtataContextBuilder}

WithPortsToIgnore(IEnumerable<int> portsToIgnore)

Specifies the ports to ignore.

Usage

AtataContext.Configure()
    .UseChrome()
        .WithArguments("disable-extensions", "start-maximized")
    .Build();

Logging

The list of logging methods of AtataContextBuilder:

public AtataContextBuilder<TLogConsumer>

AddLogConsumer<TLogConsumer>(TLogConsumer consumer)

where TLogConsumer : ILogConsumer

Adds the log consumer.

public AtataContextBuilder<ILogConsumer>

AddLogConsumer(string typeNameOrAlias)

Adds the log consumer. typeNameOrAlias can accept full type name, custom ILogConsumer alias (registered via LogConsumerAliases.Register method) or one of the predefined aliases: “trace”, “debug”, “console”, “nunit” and “nlog”.

public AtataContextBuilder<TraceLogConsumer>

AddTraceLogging()

Adds the TraceLogConsumer instance that uses System.Diagnostics.Trace class for logging.

public AtataContextBuilder<DebugLogConsumer>

AddDebugLogging()

Adds the DebugLogConsumer instance that uses System.Diagnostics.Debug class for logging.

public AtataContextBuilder<ConsoleLogConsumer>

AddConsoleLogging()

Adds the ConsoleLogConsumer instance that uses System.Console class for logging.

public AtataContextBuilder<NUnitTestContextLogConsumer>

AddNUnitTestContextLogging()

Adds the NUnitTestContextLogConsumer instance that uses NUnit.Framework.TestContext class for logging.

public AtataContextBuilder<NLogConsumer>

AddNLogLogging(string loggerName = null)

Adds the NLogConsumer instance that uses NLog.Logger class for logging.

Logging Configuration

The list of extension methods to configure ILogConsumer:

public AtataContextBuilder<TTLogConsumer>

WithoutSectionFinish<TTLogConsumer>()

Defines that the logging should not use section-like pair messages (not “Starting: {action}” and “Finished: {action} {time elapsed}”, but just “{action}”).

public AtataContextBuilder<TTLogConsumer>

WithMinLevel<TTLogConsumer>(LogLevel level)

Specifies the minimum level of the log event to write to the log. The default value is Trace.

Usage

AtataContext.Configure()
    .UseChrome()
    .LogConsumers.AddNUnitTestContext()
        .WithoutSectionFinish()
        .WithMinLevel(LogLevel.Info)
    .LogConsumers.AddDebug()
        .WithMinLevel(LogLevel.Debug)
    .Build();

Screenshots

The list of screenshot taking methods of AtataContextBuilder:

public AtataContextBuilder<TScreenshotConsumer>

AddScreenshotConsumer<TScreenshotConsumer>(TScreenshotConsumer consumer)

where TScreenshotConsumer : IScreenshotConsumer

Adds the screenshot consumer. Is used for custom screenshot processing.

public AtataContextBuilder<IScreenshotConsumer>

AddScreenshotConsumer(string typeNameOrAlias)

Adds the screenshot consumer. typeNameOrAlias can accept full type name, custom IScreenshotConsumer alias (registered via ScreenshotConsumerAliases.Register method) or predefined “file” alias.

public AtataContextBuilder<FileScreenshotConsumer>

AddScreenshotFileSaving()

Adds the FileScreenshotConsumer instance for the screenshot saving to file.

File Screenshots Configuration

By default AddScreenshotFileSaving method configures FileScreenshotConsumer with the following settings:

  • Folder path: $@"Logs\{AtataContext.BuildStart:yyyy-MM-dd HH_mm_ss}\{AtataContext.Current.TestName}"
  • File name format: $"{screenshotInfo.Number:D2} - {screenshotInfo.PageObjectFullName}{screenshotInfo.Title?.Prepend(" - ")}"
  • Image format: Png.

The list of extension methods to configure FileScreenshotConsumer:

public AtataContextBuilder<FileScreenshotConsumer>

With(ScreenshotImageFormat imageFormat)

Specifies the image format of the file screenshot consumer.

public AtataContextBuilder<FileScreenshotConsumer>

WithFolderPath(Func<string> folderPathBuilder)

Specifies the folder path builder of the file screenshot consumer.

public AtataContextBuilder<FileScreenshotConsumer>

WithFileName(Func<ScreenshotInfo, string> fileNameBuilder)

Specifies the file name builder of the file screenshot consumer.

public AtataContextBuilder<FileScreenshotConsumer>

WithFilePath(Func<ScreenshotInfo, string> filePathBuilder)

Specifies the file path builder of the file screenshot consumer.

Usage

The below example configures FileScreenshotConsumer to use separate folder for each test:

AtataContext.Configure().
    // Do some initialization.
    AddScreenshotFileSaving().
        WithFolderPath(() => $@"Logs\{AtataContext.BuildStart:yyyy-MM-dd HH_mm_ss}\{AtataContext.Current.TestName}").
        WithFileName(screenshotInfo => $"{screenshotInfo.Number:D2} - {screenshotInfo.PageObjectFullName}{screenshotInfo.Title?.Prepend(" - ")}").
    Build();

If you need to take a screenshot only on test failure, you may configure FileScreenshotConsumer to save all screenshot files to the same folder:

AtataContext.Configure().
    // Do some initialization.
    TakeScreenshotOnNUnitError().
    AddScreenshotFileSaving().
        WithFolderPath(() => $@"Logs\{AtataContext.BuildStart:yyyy-MM-dd HH_mm_ss}").
        WithFileName(screenshotInfo => $"{AtataContext.Current.TestName} - {screenshotInfo.PageObjectFullName}").
    Build();

Other

public AtataContextBuilder

UseBaseUrl(string baseUrl)

Sets the base URL.

public AtataContextBuilder

UseCulture(CultureInfo culture)

Sets the culture. The default value is CultureInfo.CurrentCulture.

public AtataContextBuilder

UseCulture(string cultureName)

Sets the culture by the name. The default value is CultureInfo.CurrentCulture.

public AtataContextBuilder

UseTestName(string testName)

Sets the name of the test.

public AtataContextBuilder

UseTestName(Func<string> testNameFactory)

Sets the factory method of the test name.

public AtataContextBuilder

UseNUnitTestName()

Defines that the name of the test should be taken from the NUnit test.

public AtataContextBuilder

UseTestSuiteName(string testSuiteName)

Sets the name of the test suite (fixture/class).

public AtataContextBuilder

UseTestSuiteName(Func<string> testSuiteNameFactory)

Sets the factory method of the test suite (fixture/class) name.

public AtataContextBuilder

UseTestSuiteType(Type testSuiteType)

Sets the type of the test suite (fixture/class).

public AtataContextBuilder

UseTestSuiteType(Func<Type> testSuiteTypeFactory)

Sets the factory method of the test suite (fixture/class) type.

public AtataContextBuilder

UseBaseRetryTimeout(TimeSpan timeout)

Sets the base retry timeout. The default value is 5 seconds

public AtataContextBuilder

UseBaseRetryInterval(TimeSpan interval)

Sets the base retry interval. The default value is 500 milliseconds.

public AtataContextBuilder

UseElementFindTimeout(TimeSpan timeout)

Sets the element find timeout. The default value is taken from AtataBuildingContext.BaseRetryTimeout, which is equal to 5 seconds by default.

public AtataContextBuilder

UseElementFindRetryInterval(TimeSpan interval)

Sets the element find retry interval. The default value is taken from AtataBuildingContext.BaseRetryInterval, which is equal to 500 milliseconds by default.

public AtataContextBuilder

UseWaitingTimeout(TimeSpan timeout)

Sets the waiting timeout. The default value is taken from AtataBuildingContext.BaseRetryTimeout, which is equal to 5 seconds by default.

public AtataContextBuilder

UseWaitingRetryInterval(TimeSpan interval)

Sets the waiting retry interval. The default value is taken from AtataBuildingContext.BaseRetryInterval, which is equal to 500 milliseconds by default.

public AtataContextBuilder

UseVerificationTimeout(TimeSpan timeout)

Sets the verification timeout. The default value is taken from AtataBuildingContext.BaseRetryTimeout, which is equal to 5 seconds by default.

public AtataContextBuilder

UseVerificationRetryInterval(TimeSpan interval)

Sets the verification retry interval. The default value is taken from AtataBuildingContext.BaseRetryInterval, which is equal to 500 milliseconds by default.

public AtataContextBuilder

OnBuilding(Action action)

Adds the action to perform during AtataContext building. It will be executed at the beginning of the build after the log is set up.

public AtataContextBuilder

OnBuilt(Action action)

Adds the action to perform after AtataContext building. It will be executed at the end of the build after the driver is created.

public AtataContextBuilder

OnDriverCreated(Action<RemoteWebDriver> action)

Adds the action to perform after the driver is created.

public AtataContextBuilder

OnDriverCreated(Action action)

Adds the action to perform after the driver is created.

public AtataContextBuilder

OnCleanUp(Action action)

Adds the action to perform during AtataContext cleanup.

public AtataContextBuilder

LogNUnitError()

Defines that an error occurred during the NUnit test execution should be added to the log during the cleanup.

public AtataContextBuilder

TakeScreenshotOnNUnitError(string title = "Failed")

Defines that an error occurred during the NUnit test execution should be captured by a screenshot during the cleanup.

public AtataContextBuilder

UseAssertionExceptionType(Type exceptionType)

Sets the type of the assertion exception. The default value is typeof(Atata.AssertionException).

public AtataContextBuilder

UseAssertionExceptionType<TException>()

Sets the type of the assertion exception. The default value is typeof(Atata.AssertionException).

public AtataContextBuilder

AddVariable(string key, object value)

Adds the variable.

public AtataContextBuilder

AddVariables(IDictionary<string, object> variables)

Adds the variables.

public AtataContextBuilder

AddSecretStringToMaskInLog(string value, string mask = "{*****}")

Adds the secret string to mask in log.

public AtataContextBuilder

UseTimeZone(TimeZoneInfo timeZone)

Sets the time zone.

public AtataContextBuilder

UseTimeZone(string timeZoneId)

Sets the time zone by identifier, which corresponds to the TimeZoneInfo.Id property.

public AtataContextBuilder

UseUtcTimeZone()

Sets the UTC time zone.

public AtataContextBuilder

Clear()

Clears the BuildingContext.

public AtataContext

Build()

Builds the AtataContext instance and sets it to AtataContext.Current property.

Go

A page object navigation starts with Go static class, or alternatively with Go property of AtataContext instance. Both Go approaches provide a set of similar methods for navigation.

Methods

public static T

To<T>(T pageObject = null, string url = null, bool navigate = true, bool temporarily = false)

where T : PageObject<T>

Navigates to the specified page object.

public static T

ToWindow<T>(T pageObject, string windowName, bool temporarily = false)

where T : PageObject<T>

Navigates to the window with the specified page object by name.

public static T

ToWindow<T>(string windowName, bool temporarily = false)

where T : PageObject<T>

Navigates to the window by name.

public static T

ToNextWindow<T>(T pageObject = null, bool temporarily = false)

where T : PageObject<T>

Navigates to the next window with the specified page object.

public static T

ToPreviousWindow<T>(T pageObject = null, bool temporarily = false)

where T : PageObject<T>

Navigates to the previous window with the specified page object.

public static void

ToUrl(string url)

Navigates to the specified URL.

public static T

ToNewWindow<T>(T pageObject = null, string url = null, bool temporarily = false)

where T : PageObject<T>

Navigates to a new window with the specified page object.

public static T

ToNewWindowAsTab<T>(T pageObject = null, string url = null, bool temporarily = false)

where T : PageObject<T>

Navigates to a new tab window with the specified page object.

Usage

Go.To<HomePage>()
    .Header.Should.Equal("Home");

Go.To<AboutPage>(url: "/about")
    .Should.Equal("About");

By Static URL

Every page object can have a static URL associated with it by UrlAttribute. This URL is consumed during Go.To action to perform a navigation. The URL can be an absolute, but it’s recommended to use a relative URL, it will be concatenated with BaseUrl to form an absolute URL.

Example

[Url("/contact")]
public class ContactPage : Page<_>
{
}
Go.To<ContactPage>();

By Dynamic URL

When a page object’s URL is dynamic (contains identifiers in path, query parameters, etc.), one of the following approaches can be used for assigning a dynamic URL to a page object.

Pass URL in Go.To Method

Go.To<UserPage>(url: $"/user/{userId}");

Set NavigationUrl

PageObject<TOwner> has the NavigationUrl property which can be assigned in a page object’s constructor. Static URL (a value of UrlAttribute) is also written to NavigationUrl in a base constructor, so you can combine static URL part with dynamic part.

Example 1
public class UserPage : Page<_>
{
    public UserPage(int? id = null)
    {
        if (id.HasValue)
            NavigationUrl = $"/user/{id}";
    }
}
Go.To(new UserPage(42));
Example 2
public class UserPage : Page<_>
{
    // Default constructor is needed for non-direct navigation, for example via link click.
    public UserPage()
    {
    }

    public UserPage(int id)
    {
        NavigationUrl = $"/user/{id}";
    }
}
Go.To(new UserPage(42));
Example 3
public class UserPage : Page<_>
{
    public static _ ById(int id) =>
        new() { NavigationUrl = $"/user/{id}" };
}
Go.To(UserPage.ById(42));
Example 4

Static URL can be combined with dynamic part.

[Url("/search")]
public class GoogleSearchPage : Page<_>
{
    public GoogleSearchPage(string query = null)
    {
        if (query != null)
            NavigationUrl += $"?q={query}"; // "/search" + $"?q={query}" = "/search?q={query}"
    }
}
Go.To<GoogleSearchPage>();
// Or:
Go.To(new GoogleSearchPage("keyword"));
Example 5

Similar to the previous example, but uses static method instead of constructor.

[Url("/search")]
public class GoogleSearchPage : Page<_>
{
    public static _ WithQuery(string query) =>
        new _().AppendNavigationUrl($"?q={query}");
}
Go.To<GoogleSearchPage>();
// Or:
Go.To(GoogleSearchPage.WithQuery("keyword"));

Transition

A transition from one page object to another is implemented via controls: Button, Link and Clickable. Also a transition can be specified via adding INavigable<,> interface to a custom control.

Example

For example, having 3 simple pages on a site:

  • Users” page with users table and “New” link that navigates to the “User Editor” page. Clicking on a user row redirects to the “User Details” page.
  • User Editor” page that contains “Name” input field and “Save” button that redirect back to “Users” page.
  • User Details” page containing the name of the user.

UsersPage.cs

using Atata;

namespace SampleApp.UITests
{
    using _ = UsersPage;

    [Url("users")]
    public class UsersPage : Page<_>
    {
        public Link<UserEditorPage, _> New { get; private set; }

        public Table<UserTableRow, _> Users { get; private set; }

        public class UserTableRow : TableRow<_>, INavigable<UserDetailsPage, _>
        {
            public Text<_> Name { get; private set; }
        }
    }
}

UserEditorPage.cs

using Atata;

namespace SampleApp.UITests
{
    using _ = UserEditorPage;

    public class UserEditorPage : Page<_>
    {
        public TextInput<_> Name { get; private set; }

        public Button<UsersPage, _> Save { get; private set; }
    }
}

UserDetailsPage.cs

using Atata;

namespace SampleApp.UITests
{
    using _ = UserDetailsPage;

    public class UserDetailsPage : Page<_>
    {
        public H1<_> Name { get; private set; }
    }
}

Usage

string userName;

Go.To<UsersPage>()
    .New.ClickAndGo() // Navigates to UserEditorPage.
        .Name.SetRandom(out userName) // Sets the random value to Name field and stores it to userName variable.
        .Save.ClickAndGo() // Clicking the Save button navigates back to UsersPage.
    .Users.Rows[x => x.Name == userName].ClickAndGo() // Clicking the row navigates to UserDetailsPage.
        .Name.Should.Equal(userName);

Currently this functionality is available only for Chrome and Edge, both local and remote.

The feature brings a monitoring of browser logs, such as warnings and errors, which happen during a test execution. Browser logs can be transferred to Atata logs and can raise warnings.

In order to enable browser logs monitoring, configure AtataContext in the following way:

AtataContext.GlobalConfiguration
    .BrowserLogs.UseLog()
    .BrowserLogs.UseMinLevelOfWarning(LogLevel.Warn);

Or in Atata JSON config:

{
  "browserLogs": {
    "log": true,
    "minLevelOfWarning": "warn" // Supports: "trace", "debug", "info", "warn", "error", "fatal".
  }
}

UseLog(bool enable = true) - sets a value indicating whether the browser log should be transferred to Atata logging system. The default value is false.

UseMinLevelOfWarning(LogLevel? minLevel) - sets the minimum log level on which to raise warnings. The default value is null, meaning that warning is disabled. For example, setting the LogLevel.Warn value will mean to warn on browser log entries with LogLevel.Warn level and higher, which are LogLevel.Error and LogLevel.Fatal.

A log entry in Atata log can look like:

2023-08-26 20:41:49.3187 TRACE - Browser log: 20:41:49.2800 ERROR http://localhost:54321/browserlogs 17:10 Uncaught Error: Some thrown error.

A warning looks this way:

Unexpected browser log error on "<ordinary>" page:
http://localhost:54321/browserlogs 17:10 Uncaught Error: Some thrown error.

The fluent API for handling JavaScript popup boxes (alert, confirm, prompt).

The base PageObject<TOwner> class has 3 methods for different popup boxes:

public AlertBox<TOwner> SwitchToAlertBox(TimeSpan? waitTimeout = null, TimeSpan? waitRetryInterval = null);

public ConfirmBox<TOwner> SwitchToConfirmBox(TimeSpan? waitTimeout = null, TimeSpan? waitRetryInterval = null);

public PromptBox<TOwner> SwitchToPromptBox(TimeSpan? waitTimeout = null, TimeSpan? waitRetryInterval = null);

The methods wait and switch to the open popup box. By default, if waitTimeout and waitRetryInterval arguments are not specified, the AtataContext.WaitingTimeout and AtataContext.WaitingRetryInterval values are used correspondingly. If popup box does not appear within the specified time, the TimeoutException is thrown.

Alert

Go.To<SomePage>()
    .AlertButton.Click()
    .SwitchToAlertBox()
    .Accept();

Confirm

Go.To<SomePage>()
    .ConfirmButton.Click()
    .SwitchToConfirmBox()
    .Accept(); // or Cancel()

Prompt

Go.To<SomePage>()
    .PromptButton.Click()
    .SwitchToPromptBox()
    .Type("Some text")
    .Accept(); // or Cancel()

Verify Popup Text

Use Text property of popup box classes to get or verify the text of popup.

page.SwitchToAlertBox()
    .Text.Should.Be("Some text");