Attributes

A way to configure page object components.

Atata attributes can divided into the categories:

  • 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.

There are 5 attributes levels (ordered by priority from lowest to highest level):

  1. Declared
  2. Parent component
  3. Assembly
  4. Global
  5. Component

Replacable attributes (FindAttribute’s, behaviors, etc.) at lower level can override similar attributes at higher levels.

Declared Level

Declared attributes are located on control properties.

[FindById("post-title")]
public TextInput<_> Title { get; private set; }

Parent Component Level

Parent pomponent attributes are located on a parent component, which can be a page object class or a wrapping control.

[FindById("post-title", TargetName = nameof(Title))]
public class SomePage : Page<_>
{
    public TextInput<_> Title { get; private set; }
}
[FindByName(TargetType = typeof(TextInput<>))]
public class UserDetailsSection<TOwner> : Control<TOwner>
    where TOwner : PageObject<TOwner>
{
    public TextInput<_> FirstName { get; private set; }

    public TextInput<_> LastName { get; private set; }
}

Assembly Level

Assembly attributes are spread across all components in the assembly where its is defined.

[assembly: Culture("en-US")]
[assembly: FindByName(TermCase.Pascal, TargetType = typeof(Input<,>))]

Global Level

Global attributes are spread across all components in scope of a certain AtataContext or all contexts if an attribute is added to AtataContext.GlobalConfiguration. Global attributes can be added though AtataContextBuilder.Attributes property or specified in JSON config file (see JSON Schema).

AtataContext.Configure()
    .Attributes.Global.Add(
        new ClicksUsingScriptAttribute())
    .Build();
AtataContext.GlobalConfiguration
    .Attributes.Global.Add(
        new FindByIdAttribute
        {
            TargetTypes = new[] { typeof(Button<>), typeof(TextInput<>) }
        });

Component Level

Component-level attributes are defined on a class of component and don’t have any target property set.

[Url("some-page")]
public class SomePage : Page<_>
{
}
[ControlDefinition("div", ContainingClass = "some-class")]
public class SomeControl<TOwner> : Control<_>
    where TOwner : PageObject<TOwner>
{
}

Almost all Atata attributes, except of few special ones, are inherited from a base MulticastAttribute.

MulticastAttribute can be applied to a component at any level (declared, parent component, assembly, global and component).

Properties

public string[] TargetNames { get; set; }

public string TargetName { get; set; }

public Type[] TargetTypes { get; set; }

public Type TargetType { get; set; }

public string[] TargetTags { get; set; }

public string TargetTag { get; set; }

public Type[] TargetParentTypes { get; set; }

public Type TargetParentType { get; set; }

public string[] ExcludeTargetNames { get; set; }

public string ExcludeTargetName { get; set; }
        
public Type[] ExcludeTargetTypes { get; set; }
        
public Type ExcludeTargetType { get; set; }
        
public string[] ExcludeTargetTags { get; set; }

public string ExcludeTargetTag { get; set; }

public Type[] ExcludeTargetParentTypes { get; set; }
        
public Type ExcludeTargetParentType { get; set; }

public bool TargetSelf { get; set; }

public bool TargetSelfAndChildren { get; set; }

public bool TargetAnyType { get; set; }

public bool TargetAllChildren { get; set; }

public bool TargetChildren { get; set; }

Example #1

Assume that we have a control class that contains Select button property:

[ControlDefinition(ContainingClass = "some-class")]
public class SomeControl<TOwner> : Control<TOwner>
    where TOwner : PageObject<TOwner>
{
    [FindFirst]
    public TextInput<TOwner> Input { get; private set; }

    [FindByContent("Select...")]
    public Button<TOwner> Select { get; private set; }
}

In most cases this button has text “Select…” that is used to identify the control. Regularly this control can be defined this way in page objects:

public SomeControl<_> Control1 { get; private set; }

Let’s also assume that in some particular page the text of this button can be different, for example “…”. So we need to change the locator of that button from [FindByContent("Select...")] to [FindByContent("...")] only for that page.

It can be done using targeting mechanics of MulticastAttribute.

[FindByContent("...", TargetName = nameof(SomeControl<_>.Select))]
public SomeControl<_> Control1 { get; private set; }

Example #2

Another example is to change the settings of ItemsControl items.

The following ItemsControl has all descendant elements as items:

[FindById("some-id")]
public ItemsControl<Control<_>, _> ItemsControlOfDescendantsAsControls { get; private set; }

But this one has only child elements as items:

[FindById("some-id")]
[FindSettings(OuterXPath = "./", TargetName = "Items")]
public ItemsControl<Control<_>, _> ItemsControlOfChildrenAsControls { get; private set; }

This is the functionality to add extra attributes to components via AtataContext. These added attributes has higher order level and can override the attributes of the same kind (and at the same level) declared in a regular manner, for example FindAttributes or behavior attributes.

This feature can be quite useful in testing multi-language applications or applications that have some HTML differences on different environments/version/editions. So you might need to configure the page objects in a bit different way depending on testing environment.

Examples

Apply to Control

AtataContext.GlobalConfiguration
    .Attributes.Component(typeof(Button<>)).Add(
        new FindByIdAttribute());

Sets all buttons to be found by id attribute.

Apply to Several Controls

AtataContext.GlobalConfiguration
    .Attributes.Global.Add(
        new FindByIdAttribute
        {
            TargetTypes = new[] { typeof(Button<>), typeof(TextInput<>) }
        });

Sets all buttons and text inputs to be found by id attribute.

Apply to Page Object

AtataContext.GlobalConfiguration
    .Attributes.Component<SomePage>().Add(
        new WaitForAngularAttribute());

Adds WaitForAngularAttribute trigger to SomePage.

Apply to Control Property of Page Object

AtataContext.GlobalConfiguration
    .Attributes.Component<SomePage>()
        .Property(x => x.Save).Add(new FindByContentAttribute("Save"))
        .Property(x => x.Cancel).Add(new FindByContentAttribute("Cancel"));

Apply to Components Within Assembly

AtataContext.GlobalConfiguration
    .Attributes.Assembly("SomeProduct.SomeAtataComponentsLibrary").Add(
        new WaitForAngularAttribute
        {
            TargetType = typeof(Page<>)
        });

Adds WaitForAngularAttribute trigger to all page classes that are located in “SomeProduct.SomeAtataComponentsLibrary” library (project).