In this walkthrough, you'll gain experience setting a custom field on a dialog and overriding default implementations. The UAT Automation Kit provides multiple ways to achieve a result, and this walkthrough describes two potential implementations.
This tutorial guides you through the steps to create a test to add an individual when the Add an individual screen has been customized to include custom fields. It describes two potential implementations to handle a dialog with a custom field, but these are not the only approaches. In this walkthrough, you will:
And this is a customized version of the Add an individual screen that includes additional fields.
In our test project, we can create a feature file to create a behavior-driven development test with Gherkin.
@DelvingDeeper
Scenario: Add an individual on a dialog containing a custom field.
Given I have logged into BBCRM
When I add constituent
| Last name | First name | Title | Nickname | Information source | Country of Origin | Matriculation Year (Use) |
| Parker | Peter | Mr. | Spiderman | Friend | United States | 2014 |
Then a constituent is created
Then we can create a step file and populate its steps.
using System;
using System.Collections.Generic;
using Blackbaud.UAT.Base;
using Blackbaud.UAT.Core.Base;
using Blackbaud.UAT.Core.Crm;
using TechTalk.SpecFlow;
namespace Delving_Deeper
{
[ Binding]
public class SampleTestsSteps : BaseSteps
{
[Given(@"I have logged into BBCRM")]
public void GivenIHaveLoggedIntoBBCRM()
{
BBCRMHomePage.Login();
}
[When(@"I add constituent")]
public void WhenIAddConstituent(Table constituents)
{
foreach (var constituent in constituents.Rows)
{
BBCRMHomePage.OpenConstituentsFA();
ConstituentsFunctionalArea.AddAnIndividual(groupCaption: "Add Records");
IndividualDialog.SetIndividualFields(constituent);
IndividualDialog.Save();
}
}
[Then(@"a constituent is created")]
public void ThenAConstituentIsCreated()
{
if (!BaseComponent.Exists(Panel.getXPanelHeader("individual"))) FailTest("A constituent panel did not load.");
}
}
}
ConstituentsFunctionalArea.AddAnIndividual()
groupCaption
If we run the test against the customization and its custom field at this point, an error indicates that we need custom support for the new field.
using System.Collections.Generic;
using Blackbaud.UAT.Base;
using Blackbaud.UAT.Core.Crm;
using TechTalk.SpecFlow;
namespace Delving_Deeper
{
public class CustomIndividualDialog : IndividualDialog
{
private static readonly IDictionary<string, CrmField> CustomSupportedFields = new Dictionary<string, CrmField>
{
{"Country of Origin", new CrmField("_ATTRIBUTECATEGORYVALUE0_value", FieldType.Dropdown)},
{"Matriculation Year (Use)", new CrmField("_ATTRIBUTECATEGORYVALUE1_value", FieldType.Dropdown)}
};
}
}
using System.Collections.Generic;
using Blackbaud.UAT.Base;
using Blackbaud.UAT.Core.Crm;
using TechTalk.SpecFlow;
namespace Delving_Deeper
{
public class CustomIndividualDialog : IndividualDialog
{
private static readonly IDictionary<string, CrmField> CustomSupportedFields = new Dictionary<string, CrmField>
{
{"Country of Origin", new CrmField("_ATTRIBUTECATEGORYVALUE0_value", FieldType.Dropdown)},
{"Matriculation Year (Use)", new CrmField("_ATTRIBUTECATEGORYVALUE1_value", FieldType.Dropdown)}
};
}
}
id
_ATTRIBUTECATEGORYVALUE0_value
_ATTRIBUTECATEGORYVALUE1_value
IndividualDialog
SetFields()
SetFields
IDictionary
CrmFields
{
SetFields(GetDialogId(DialogIds), fields, SupportedFields, CustomSupportedFields);
}
CustomSupportedFields
SupportedFields
CustomSupportedFields
CustomIndividualDIalog
SetIndividualFields
[When(@"I add constituent")]
public void WhenIAddConstituent(Table constituents)
{
foreach (var constituent in constituents.Rows)
{
BBCRMHomePage.OpenConstituentsFA();
constituent["Last name"] += uniqueStamp;
ConstituentsFunctionalArea.AddAnIndividual(groupCaption: "Add Records");
CustomIndividualDialog.SetIndividualFields(constituent);
IndividualDialog.Save();
}
}
Save
The test should pass now.
@DelvingDeeper
Scenario: Add an individual on a dialog containing a custom field using a custom method
Given I have logged into BBCRM
When I start to add a constituent
| Last name | First name | Title | Nickname | Information source |
| Parker | Peter | Mr. | Spiderman | Friend |
And I set the custom constituent field "Country of Origin" to "Argentina"
And I set the custom constituent field "Matriculation Year (Use)" to "2012"
And I save the add an individual dialog
Then a constituent is created
CustomIndividualDialog
public static void SetCustomField(string fieldCaption, string value)
{
//Use the same IDictionary<string, CrmField> CustomSupportedFields from the Overload Approach
SetField(GetDialogId(DialogIds), fieldCaption, value, CustomSupportedFields);
}
new
new
[When(@"I start to add a constituent")]
public void WhenIStartToAddAConstituent(Table constituents)
{
foreach (var constituent in constituents.Rows)
{
BBCRMHomePage.OpenConstituentsFA();
constituent["Last name"] += uniqueStamp;
ConstituentsFunctionalArea.AddAnIndividual(groupCaption: "Add Records");
IndividualDialog.SetIndividualFields(constituent);
}
}
[When(@"I set the custom constituent field ""(.*)"" to ""(.*)""")]
public void WhenISetTheCustomConstituentFieldTo(string fieldCaption, string value)
{
CustomIndividualDialog.SetCustomField(fieldCaption, value);
}
[When(@"I save the add an individual dialog")]
public void WhenISaveTheAddAnIndividualDialog()
{
IndividualDialog.Save();
}
The test should pass now.
@DelvingDeeper
Scenario: Set the Last/Org/Group name field in a constituent search dialog
Given I have logged into BBCRM
When I open the constituent search dialog
And set the Last/Org/Group name field value to "Smith"
Then the Last/Org/Group name field is "Smith"
[When(@"I open the constituent search dialog")]
public void WhenIOpenTheConstituentSearchDialog()
{
BBCRMHomePage.OpenConstituentsFA();
FunctionalArea.OpenLink("Constituents", "Constituent search");
}
[When(@"set the Last/Org/Group name field value to ""(.*)""")]
public void WhenSetTheLastOrgGroupNameFieldValueTo(string fieldValue)
{
SearchDialog.SetTextField(Dialog.getXInput("ConstituentSearchbyNameorLookupID", "KEYNAME"), fieldValue);
}
[Then(@"the Last/Org/Group name field is ""(.*)""")]
public void ThenTheLastOrgGroupNameFieldIs(string expectedValue)
{
SearchDialog.ElementValueIsSet(Dialog.getXInput("ConstituentSearchbyNameorLookupID", "KEYNAME"), expectedValue);
}
When run against a standard Blackbaud CRM application, the test passes.
The steps navigate to the >Constituents functional area and click the Constituent search task.
The Last/Org/Group name field is set and validated as containing the desired value.
If we run the test against a custom application whose Constituent functional area looks like this...
... then we get the following error:
We can resolve this with the following code edit.
When(@"I open the constituent search dialog")]
public void WhenIOpenTheConstituentSearchDialog()
{
BBCRMHomePage.OpenConstituentsFA();
FunctionalArea.OpenLink("Searching", "Constituent search");
}
If we run the test now, we get a new error.
Another customization must exist. The error stack trace indicates that the XPath constructor for the Last/Org/Group name field is not compatible with this application. NoSuchElementExceptions
Inspecting the XPaths to get to the customized dialog requires us to use a more direct XPath trace.
BaseComponent
SetTextField()
ElementValueIsSet()
[When(@"set the Last/Org/Group name field value to ""(.*)""")]
public void WhenSetTheLastOrgGroupNameFieldValueTo(string fieldValue)
{
BaseComponent.SetTextField("//div[contains@class, 'bbui-dialog') and contain(@style, 'visible')]//input[contains(@id, 'KEYNAME'))]", fieldValue);
}
[Then(@"the Last/Org/Group name field is ""(.*)""")]
public void ThenTheLastOrgGroupNameFieldIs(string expectedValue)
{
BaseComponent.ElementValueIsSet("//div[contains@class, 'bbui-dialog') and contain(@style, 'visible')]//input[contains(@id, 'KEYNAME'))]", expectedValue);
}
The test should pass now.
@DelvingDeeper
Scenario: Add an individual and set the related individual through the constituent search list
Given I have logged into BBCRM
And a constituent exists
| Last name | First name |
| LeBouf | Shia |
When I start to add a constituent
| Last name | First name |
| Prime | Optimus |
And set the household fields
| Related individual | Individual is the | Related individual is the |
| LeBouf | Co-worker | Co-worker |
And I save the add an individual dialog
Then a constituent is created
using System;
using System.Collections.Generic;
using Blackbaud.UAT.Base;
using Blackbaud.UAT.Core.Base;
using Blackbaud.UAT.Core.Crm;
using TechTalk.SpecFlow;
namespace Delving_Deeper
{
[Binding]
public class SampleTestsSteps : BaseSteps
{
[Given(@"I have logged into BBCRM")]
public void GivenIHaveLoggedIntoBBCRM()
{
BBCRMHomePage.Login();
}
[Then(@"a constituent is created")]
public void ThenAConstituentIsCreated()
{
if (!BaseComponent.Exists(Panel.getXPanelHeader("individual"))) FailTest("A constituent panel did not load.");
}
[When(@"I start to add a constituent")]
public void WhenIStartToAddAConstituent(Table constituents)
{
foreach (var constituent in constituents.Rows)
{
BBCRMHomePage.OpenConstituentsFA();
constituent["Last name"] += uniqueStamp;
ConstituentsFunctionalArea.AddAnIndividual();
IndividualDialog.SetIndividualFields(constituent);
}
}
[When(@"I save the add an individual dialog")]
public void WhenISaveTheAddAnIndividualDialog()
{
IndividualDialog.Save();
}
[Given(@"a constituent exists")]
public void GivenAConstituentExists(Table constituents)
{
foreach (var constituent in constituents.Rows)
{
BBCRMHomePage.OpenConstituentsFA();
constituent["Last name"] += uniqueStamp;
ConstituentsFunctionalArea.AddAnIndividual(constituent);
}
}
[When(@"set the household fields")]
public void WhenSetTheHouseholdFields(Table fieldsTable)
{
foreach (var fieldValues in fieldsTable.Rows)
{
IndividualDialog.SetHouseholdFields(fieldValues);
}
}
}
}
At some point in the test, the Related individual field on the Add an individual dialog is set by using the associated searchlist.
What if we want to set the field through the Add button? This requires us to override the default implementation for how the Related individual field is set.
If you have not created the CustomIndividualDialog
using System.Collections.Generic;
using Blackbaud.UAT.Base;
using Blackbaud.UAT.Core.Crm;
using TechTalk.SpecFlow;
namespace Delving_Deeper
{
public class CustomIndividualDialog : IndividualDialog
{
public new static void SetHouseholdFields(TableRow fields)
{
OpenTab("Household");
}
}
}
Next we specify custom logic if a value for the Related individual field is provided. If a value is provided for this field, we click the button that brings up the add dialog. Be sure to read the API documentation for the XPath constructors.
public new static void SetHouseholdFields(TableRow fields)
{
OpenTab("Household");
if (fields["Related individual"] != null)
{
WaitClick(getXInputNewFormTrigger(getXInput(GetDialogId(DialogIds), "_SPOUSEID_value")));
}
}
This is the resulting dialog from clicking the add button on the Related individual field.
We then set the Last name field value to the value provided for Related individual before clicking OK. We could have defined any logic and interactions involving this dialog, but let's keep it simple.
{
OpenTab("Household");
if (fields["Related individual"] != null)
{
WaitClick(getXInputNewFormTrigger(getXInput(GetDialogId(DialogIds), "_SPOUSEID_value")));
SetTextField(getXInput("IndividualSpouseBusinessSpouseForm", "_SPOUSE_LASTNAME_value"), fields["Related individual"]);
OK();
}
}
Before we call the base implementation to set the rest of the fields, we set fields["Related individual"] = null
SetHouseholdFields
using System.Collections.Generic;
using Blackbaud.UAT.Base;
using Blackbaud.UAT.Core.Crm;
using TechTalk.SpecFlow;
namespace Delving_Deeper
{
public class CustomIndividualDialog : IndividualDialog
{
public new static void SetHouseholdFields(TableRow fields)
{
OpenTab("Household");
if (fields["Related individual"] != null)
{
WaitClick(getXInputNewFormTrigger(getXInput(GetDialogId(dialogIds), "_SPOUSEID_value")));
SetTextField(getXInput("IndividualSpouseBusinessSpouseForm", "_SPOUSE_LASTNAME_value"), fields["Related individual"]);
OK();
fields["Related individual"] = null;
}
IndividualDialog.SetHouseholdFields(fields);
}
}
}
An alternative solution would be to remove the Related individual key from the fields object.
fields.Keys.Remove("Related individual");
[When(@"set the household fields")]
public void WhenSetTheHouseholdFields(Table fieldsTable)
{
foreach (var fieldValues in fieldsTable.Rows)
{
CustomIndividualDialog.SetHouseholdFields(fieldValues);
}
}
The test now sets the Related individual field through the add button and not the search dialog.