Skip to content

Commit dcc353e

Browse files
ewingjmmjahlvosagiestartdashworthleroi-douglas
authored
feat: 2021 release wave 2 (#119)
Co-authored-by: Mike Andrews <mja.hlv@gmail.com> Co-authored-by: Osagie Okoedo <osagie.okoedo@capgemini.com> Co-authored-by: Tom Ashworth <tom.ashworth@capgemini.com> Co-authored-by: Douglas <leroy.douglas@capgemini.com>
1 parent 0dccf41 commit dcc353e

29 files changed

Lines changed: 18075 additions & 395 deletions

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Installing the NuGet package creates a _power-apps-bindings.yml_ file in your pr
5555
```yaml
5656
url: SPECFLOW_POWERAPPS_URL # mandatory
5757
useProfiles: false # optional - defaults to false if not set
58+
deleteTestData: true # optional - defaults to true if not set
5859
browserOptions: # optional - will use default EasyRepro options if not set
5960
browserType: Chrome
6061
headless: true
@@ -138,6 +139,8 @@ These bindings look for a corresponding JSON file in a _data_ folder in the root
138139
with a difference.json
139140
```
140141

142+
The deleteTestData property in the power-apps-bindings.yml file can be set to specify whether you want records created via these bindings to be deleted after a scenario has ran. You may wish to override the default value and retain these e.g. to aid in diagnosing failures.
143+
141144
If you are using the binding which creates data as someone other than the current user, you will need the following configuration to be present:
142145

143146
- a user with a matching alias in the `users` array that has the `username` set

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Capgemini.PowerApps.SpecFlowBindings.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
</Target>
4646

4747
<ItemGroup>
48-
<PackageReference Include="Dynamics365.UIAutomation.Api" Version="9.2.21014.138" />
48+
<PackageReference Include="Dynamics365.UIAutomation.Api" Version="9.2.21101.119-RW2-Preview" />
4949
<PackageReference Include="FluentAssertions" Version="5.10.3" />
5050
<PackageReference Include="Microsoft.Build.Tasks.Git" Version="1.0.0">
5151
<PrivateAssets>all</PrivateAssets>

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Configuration/TestConfiguration.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ public TestConfiguration()
4545
[YamlMember(Alias = "useProfiles")]
4646
public bool UseProfiles { get; set; } = false;
4747

48+
/// <summary>
49+
/// Gets or sets a value indicating whether to delete test data.
50+
/// </summary>
51+
[YamlMember(Alias = "deleteTestData")]
52+
public bool DeleteTestData { get; set; } = true;
53+
4854
/// <summary>
4955
/// Gets or sets the base path where the user profiles are stored.
5056
/// </summary>

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/SubGridExtensions.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public static int GetRecordIndexById(this SubGrid subGrid, string subgridName, I
2727
throw new ArgumentNullException(nameof(driver));
2828
}
2929

30-
driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Grid.Container]));
30+
driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subgridName)));
3131

3232
var index = (long)driver.ExecuteScript(
3333
$"return Xrm.Page.getControl(\"{subgridName}\").getGrid().getRows().get().findIndex(row => row.getData().getEntity().getId() == \"{recordId.ToString("B").ToUpper(CultureInfo.CurrentCulture)}\")");
@@ -52,8 +52,7 @@ public static void HighlightRecord(this SubGrid subGrid, string subgridName, IWe
5252

5353
var subGridElement = driver.FindElement(
5454
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subgridName)));
55-
56-
var rows = subGridElement.FindElements(By.CssSelector("div.wj-row[role=row][data-lp-id]"));
55+
var rows = subGridElement.FindElements(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridRows]));
5756

5857
if (rows.Count == 0)
5958
{
@@ -65,7 +64,7 @@ public static void HighlightRecord(this SubGrid subGrid, string subgridName, IWe
6564
throw new IndexOutOfRangeException($"Subgrid {subgridName} record count: {rows.Count}. Expected: {index + 1}");
6665
}
6766

68-
rows[index].FindElement(By.TagName("div")).Click();
67+
rows[index].FindElements(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCells]))[0].Click();
6968
driver.WaitForTransaction();
7069
}
7170
}

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Hooks/AfterScenarioHooks.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ public static void TestCleanup()
3131
{
3232
try
3333
{
34-
TestDriver.DeleteTestData();
34+
if (TestConfig.DeleteTestData)
35+
{
36+
TestDriver.DeleteTestData();
37+
}
3538
}
3639
catch (WebDriverException)
3740
{

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Hooks/BeforeRunHooks.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Capgemini.PowerApps.SpecFlowBindings.Configuration;
88
using Capgemini.PowerApps.SpecFlowBindings.Steps;
99
using Microsoft.Dynamics365.UIAutomation.Api.UCI;
10+
using Microsoft.Dynamics365.UIAutomation.Browser;
1011
using TechTalk.SpecFlow;
1112

1213
/// <summary>
@@ -54,10 +55,10 @@ public static void BaseProfileSetup()
5455
userBrowserOptions.Headless = true;
5556

5657
var webClient = new WebClient(userBrowserOptions);
57-
using (new XrmApp(webClient))
58+
using (var app = new XrmApp(webClient))
5859
{
5960
var user = TestConfig.Users.First(u => u.Username == username);
60-
LoginSteps.Login(webClient.Browser.Driver, TestConfig.GetTestUrl(), user.Username, user.Password);
61+
app.OnlineLogin.Login(TestConfig.GetTestUrl(), user.Username.ToSecureString(), user.Password.ToSecureString());
6162
}
6263
}
6364
finally

bindings/src/Capgemini.PowerApps.SpecFlowBindings/PowerAppsStepDefiner.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ protected static string AccessToken
5252
{
5353
var hostSegments = TestConfig.GetTestUrl().Host.Split('.');
5454

55-
return GetApp().AcquireTokenForClient(new string[] { $"https://{hostSegments[0]}.api.{hostSegments[1]}.dynamics.com//.default" })
55+
return GetApp()
56+
.AcquireTokenForClient(new string[] { $"https://{hostSegments[0]}.api.{hostSegments[1]}.dynamics.com//.default" })
5657
.ExecuteAsync()
5758
.Result.AccessToken;
5859
}
@@ -212,12 +213,21 @@ protected static IDictionary<string, string> UserProfileDirectories
212213
/// </summary>
213214
protected static void Quit()
214215
{
215-
var driver = client?.Browser?.Driver;
216-
217-
xrmApp?.Dispose();
216+
// Try to dispose, and catch web driver errors that can occur on disposal. Retry the disposal if these occur. Trap the final exception and continue the disposal process.
217+
var polly = Policy
218+
.Handle<WebDriverException>()
219+
.Retry(3, (ex, i) =>
220+
{
221+
Console.WriteLine(ex.Message);
222+
})
223+
.ExecuteAndCapture(() =>
224+
{
225+
xrmApp?.Dispose();
218226

219-
// Ensuring that the driver gets disposed. Previously we were left with orphan processes and were unable to clean up profile folders.
220-
driver?.Dispose();
227+
// Ensuring that the driver gets disposed. Previously we were left with orphan processes and were unable to clean up profile folders. We cannot rely on xrmApp.Dispose to properly dispose of the web driver.
228+
var driver = client?.Browser?.Driver;
229+
driver?.Dispose();
230+
});
221231

222232
xrmApp = null;
223233
client = null;

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/EntitySteps.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ public static void ThenICanNotSeeTheField(string fieldName)
489489
[Then("the status of the record is (active|inactive)")]
490490
public static void ThenTheStatusOfTheRecordIs(string status)
491491
{
492-
XrmApp.Entity.GetFooterStatusValue().Should().BeEquivalentTo(status);
492+
XrmApp.Entity.GetFormState().Should().BeEquivalentTo(status);
493493
}
494494

495495
private static void SetFieldValue(string fieldName, string fieldValue, string fieldType)

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/EntitySubGridSteps.cs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ public class EntitySubGridSteps : PowerAppsStepDefiner
2525
[When(@"I click the '(.*)' command on the '(.*)' subgrid")]
2626
public static void WhenISelectTheCommandOnTheSubgrid(string commandName, string subGridName)
2727
{
28-
Driver.WaitUntilVisible(
29-
By.CssSelector($"div#dataSetRoot_{subGridName} button[aria-label=\"{commandName}\"]"));
28+
Driver.WaitUntilAvailable(
29+
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subGridName)));
3030

3131
XrmApp.Entity.SubGrid.ClickCommand(subGridName, commandName);
3232
}
@@ -220,10 +220,13 @@ public static void ThenTheSubgridContainsRecordsWithInTheField(string subGridNam
220220
[Then(@"I can see the '(.*)' command on the '(.*)' subgrid")]
221221
public static void ThenICanSeeTheCommandOnTheSubgrid(string commandName, string subGridName)
222222
{
223-
Driver.WaitUntilVisible(
224-
By.CssSelector($"div#dataSetRoot_{subGridName} button[aria-label=\"{commandName}\"]"),
225-
new TimeSpan(0, 0, 5),
226-
$"Could not find the {commandName} command on the {subGridName} subgrid.");
223+
Driver
224+
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subGridName)))
225+
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCommandBar]))
226+
.WaitUntilVisible(
227+
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCommandLabel].Replace("[NAME]", commandName)),
228+
new TimeSpan(0, 0, 5),
229+
$"Could not find the {commandName} command on the {subGridName} subgrid.");
227230
}
228231

229232
/// <summary>
@@ -234,8 +237,6 @@ public static void ThenICanSeeTheCommandOnTheSubgrid(string commandName, string
234237
[When(@"I click the '([^']+)' flyout on the '([^']+)' subgrid")]
235238
public static void WhenIClickTheFlyoutOnTheSubgrid(string flyoutName, string subGridName)
236239
{
237-
Driver.WaitUntilVisible(By.CssSelector($"div#dataSetRoot_{subGridName} li[aria-label=\"{flyoutName}\"]"));
238-
239240
XrmApp.Entity.SubGrid.ClickCommand(subGridName, flyoutName);
240241
}
241242

@@ -247,8 +248,11 @@ public static void WhenIClickTheFlyoutOnTheSubgrid(string flyoutName, string sub
247248
[Then(@"I can not see the '(.*)' command on the '(.*)' subgrid")]
248249
public static void ThenICanNotSeeTheCommandOnTheSubgrid(string commandName, string subGridName)
249250
{
250-
Driver.WaitUntilVisible(
251-
By.CssSelector($"div#dataSetRoot_{subGridName} button[aria-label=\"{commandName}\"]"),
251+
Driver
252+
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subGridName)))
253+
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCommandBar]))
254+
.WaitUntilVisible(
255+
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCommandLabel].Replace("[NAME]", commandName)),
252256
new TimeSpan(0, 0, 5))
253257
.Should().BeNull();
254258
}
@@ -260,10 +264,12 @@ public static void ThenICanNotSeeTheCommandOnTheSubgrid(string commandName, stri
260264
[Then(@"I can see the '(.*)' command on the flyout of the subgrid")]
261265
public static void ThenICanSeeTheCommandOnTheFlyoutOfTheSubgrid(string commandName)
262266
{
263-
Driver.WaitUntilVisible(
264-
By.CssSelector($"#__flyoutRootNode button[aria-label$='{commandName}']"),
265-
new TimeSpan(0, 0, 10),
266-
$"Could not find the {commandName} command on the flyout of the subgrid.");
267+
Driver
268+
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowContainer]))
269+
.WaitUntilVisible(
270+
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", commandName)),
271+
new TimeSpan(0, 0, 10),
272+
$"Could not find the {commandName} command on the flyout of the subgrid.");
267273
}
268274

269275
/// <summary>
@@ -274,10 +280,12 @@ public static void ThenICanSeeTheCommandOnTheFlyoutOfTheSubgrid(string commandNa
274280
public static void ThenICanNotSeeTheCommandOnTheFlyoutOfTheSubgrid(string commandName)
275281
{
276282
Driver
277-
.Invoking(d => d.WaitUntilVisible(
278-
By.CssSelector($"#__flyoutRootNode button[aria-label$=\"{commandName}\"]"),
279-
new TimeSpan(0, 0, 1),
280-
$"Could not find the {commandName} command on the flyout of the subgrid."))
283+
.Invoking(d => d
284+
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowContainer]))
285+
.WaitUntilVisible(
286+
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", commandName)),
287+
new TimeSpan(0, 0, 1),
288+
$"Could not find the {commandName} command on the flyout of the subgrid."))
281289
.Should()
282290
.Throw<Exception>();
283291
}

bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/GlobalSearchSteps.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,6 @@ public static void WhenIPerformASearch(string filterValue)
3939
XrmApp.GlobalSearch.Search(filterValue);
4040
}
4141

42-
/// <summary>
43-
/// Performs an advanced search using the filter attribute and a filter value.
44-
/// </summary>
45-
/// <param name="filterValue">Attribute filter value.</param>
46-
[When("I change the search type using the filter '(.*)'")]
47-
public static void WhenIChangeTheSearchType(string filterValue)
48-
{
49-
XrmApp.GlobalSearch.ChangeSearchType(filterValue);
50-
}
51-
5242
/// <summary>
5343
/// Open a record from a global search at a certain row.
5444
/// </summary>

0 commit comments

Comments
 (0)