Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 2021 release wave 2 #119

Merged
merged 43 commits into from
Aug 4, 2022
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
bdd23c4
feat: 2021 wave 2
ewingjm Oct 19, 2021
ad69e12
ci: temporarily disable tests
ewingjm Oct 20, 2021
d154866
ci: disable timing out WhiteSource Bolt task
ewingjm Oct 20, 2021
b838da1
feat: toggle test data cleanup
ewingjm Jan 7, 2022
7a599b2
feat: handle webdriverexceptions on quit
mjahlv Mar 23, 2022
add682a
feat: readded disposal of driver
mjahlv Mar 29, 2022
d36c266
Merge remote-tracking branch 'origin/master' into feat/2021-release-w…
ewingjm Jul 4, 2022
ed507f6
Revert "ci: temporarily disable tests"
ewingjm Jul 4, 2022
c0ea1ac
refactor: update ChromeDriver dependency to v103
ewingjm Jul 4, 2022
6eae0f9
ci: fix output variables not set correctly
ewingjm Jul 4, 2022
f9d349a
ci: fix broken environment variable syntax
ewingjm Jul 4, 2022
9892662
Bug: Apply the correct selector on the 'Add button on lookup dialogue'
osagiestar Jul 15, 2022
b210f20
test: Update all user settings to English (United Kingdom)
tdashworth Jul 18, 2022
32cd45b
ci: debug connectionstring
tdashworth Jul 18, 2022
51d95e5
ci: revert change to build tools environment variables
tdashworth Jul 18, 2022
2be7147
ci: switch to admin user details
tdashworth Jul 18, 2022
daee3a6
ci: correct enum reference
tdashworth Jul 18, 2022
b0338d8
Merge branch 'feat/2021-release-wave-2' of https://github.com/Capgemi…
osagiestar Jul 19, 2022
8b1fd15
fix: Ammended ThenICanSeeTheGroup() so that XPath now searches for h3
leroi-douglas Jul 20, 2022
dbd6c73
Merge branch 'feat/2021-release-wave-2' of github.com:Capgemini/power…
leroi-douglas Jul 20, 2022
ed39ff8
ci: 3 parallel test runs
tdashworth Jul 21, 2022
cc2aab7
EasyRepro issues need to be fixed before we can carry out further tes…
osagiestar Jul 27, 2022
9913682
fix: refresh app access token (#125)
leroy-douglas Jul 28, 2022
c98aa47
test: extend crm timeout to 10 minutes for solution install
tdashworth Jul 28, 2022
44b0652
test: extend crm timeout to 10 minutes for solution install
tdashworth Jul 28, 2022
03f0fb1
test: solution install with async
tdashworth Jul 29, 2022
c092bc1
test: solution install
tdashworth Jul 29, 2022
9d40ef8
Add 5 minutes to current time as a safeguard when checking access tok…
leroi-douglas Aug 2, 2022
c67498d
Updated to the latest version of EasyRepro
leroi-douglas Aug 2, 2022
dcd677a
Flagged various tests that are failing due to EasyRepro
leroi-douglas Aug 2, 2022
a84106e
downgraded the EasyRepro version back to 9.2.21101.119-RW2-Preview, a…
leroi-douglas Aug 3, 2022
80fb98a
Applied a little refactoring to ImportMockSolution()
leroi-douglas Aug 3, 2022
bc8f3d9
SonarCloudAnalyze, SonarCloudPublish and WhiteSource Bolt tasks are n…
leroi-douglas Aug 3, 2022
d11af59
replaced username & password authentication with clientId and clientS…
leroi-douglas Aug 3, 2022
e6457d6
changed the connection string to use clientId and ClientSecret
leroi-douglas Aug 3, 2022
29b9064
replaced the -group for the ProvisionEnvironment stage
leroi-douglas Aug 3, 2022
e34aed6
clientSecret variable has quotes to protect against special charatcters
leroi-douglas Aug 3, 2022
21f71e0
clientSecret variable now using double quotes with, also added missin…
leroi-douglas Aug 3, 2022
2cca0a2
refactor: use window.top for all driver objects
ewingjm Aug 3, 2022
5e89993
refactor: revert access token related changes
ewingjm Aug 3, 2022
52da5a4
fix: add missing line
ewingjm Aug 3, 2022
bf9e56a
fix: erronous var keyword
ewingjm Aug 3, 2022
e7d9c84
ci: force sync of test user
ewingjm Aug 4, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Installing the NuGet package creates a _power-apps-bindings.yml_ file in your pr
```yaml
url: SPECFLOW_POWERAPPS_URL # mandatory
useProfiles: false # optional - defaults to false if not set
deleteTestData: true # optional - defaults to true if not set
browserOptions: # optional - will use default EasyRepro options if not set
browserType: Chrome
headless: true
Expand Down Expand Up @@ -138,6 +139,8 @@ These bindings look for a corresponding JSON file in a _data_ folder in the root
with a difference.json
```

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.

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:

- a user with a matching alias in the `users` array that has the `username` set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
</Target>

<ItemGroup>
<PackageReference Include="Dynamics365.UIAutomation.Api" Version="9.2.21014.138" />
<PackageReference Include="Dynamics365.UIAutomation.Api" Version="9.2.21101.119-RW2-Preview" />
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Microsoft.Build.Tasks.Git" Version="1.0.0">
<PrivateAssets>all</PrivateAssets>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ public TestConfiguration()
[YamlMember(Alias = "useProfiles")]
public bool UseProfiles { get; set; } = false;

/// <summary>
/// Gets or sets a value indicating whether to delete test data.
/// </summary>
[YamlMember(Alias = "deleteTestData")]
public bool DeleteTestData { get; set; } = true;

/// <summary>
/// Gets or sets the base path where the user profiles are stored.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static int GetRecordIndexById(this SubGrid subGrid, string subgridName, I
throw new ArgumentNullException(nameof(driver));
}

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

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

var subGridElement = driver.FindElement(
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subgridName)));

var rows = subGridElement.FindElements(By.CssSelector("div.wj-row[role=row][data-lp-id]"));
var rows = subGridElement.FindElements(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridRows]));

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

rows[index].FindElement(By.TagName("div")).Click();
rows[index].FindElements(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCells]))[0].Click();
driver.WaitForTransaction();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ public static void TestCleanup()
{
try
{
TestDriver.DeleteTestData();
if (TestConfig.DeleteTestData)
{
TestDriver.DeleteTestData();
}
}
catch (WebDriverException)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Capgemini.PowerApps.SpecFlowBindings.Configuration;
using Capgemini.PowerApps.SpecFlowBindings.Steps;
using Microsoft.Dynamics365.UIAutomation.Api.UCI;
using Microsoft.Dynamics365.UIAutomation.Browser;
using TechTalk.SpecFlow;

/// <summary>
Expand Down Expand Up @@ -54,10 +55,10 @@ public static void BaseProfileSetup()
userBrowserOptions.Headless = true;
var webClient = new WebClient(userBrowserOptions);
using (new XrmApp(webClient))
using (var app = new XrmApp(webClient))
{
var user = TestConfig.Users.First(u => u.Username == username);
LoginSteps.Login(webClient.Browser.Driver, TestConfig.GetTestUrl(), user.Username, user.Password);
app.OnlineLogin.Login(TestConfig.GetTestUrl(), user.Username.ToSecureString(), user.Password.ToSecureString());
}
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ protected static string AccessToken
{
var hostSegments = TestConfig.GetTestUrl().Host.Split('.');

return GetApp().AcquireTokenForClient(new string[] { $"https://{hostSegments[0]}.api.{hostSegments[1]}.dynamics.com//.default" })
return GetApp()
.AcquireTokenForClient(new string[] { $"https://{hostSegments[0]}.api.{hostSegments[1]}.dynamics.com//.default" })
.ExecuteAsync()
.Result.AccessToken;
ewingjm marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down Expand Up @@ -212,12 +213,21 @@ protected static IDictionary<string, string> UserProfileDirectories
/// </summary>
protected static void Quit()
{
var driver = client?.Browser?.Driver;

xrmApp?.Dispose();
// 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.
var polly = Policy
.Handle<WebDriverException>()
.Retry(3, (ex, i) =>
{
Console.WriteLine(ex.Message);
})
.ExecuteAndCapture(() =>
{
xrmApp?.Dispose();
// Ensuring that the driver gets disposed. Previously we were left with orphan processes and were unable to clean up profile folders.
driver?.Dispose();
// 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.
var driver = client?.Browser?.Driver;
driver?.Dispose();
});

xrmApp = null;
client = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ public static void ThenICanNotSeeTheField(string fieldName)
[Then("the status of the record is (active|inactive)")]
public static void ThenTheStatusOfTheRecordIs(string status)
{
XrmApp.Entity.GetFooterStatusValue().Should().BeEquivalentTo(status);
XrmApp.Entity.GetFormState().Should().BeEquivalentTo(status);
}

private static void SetFieldValue(string fieldName, string fieldValue, string fieldType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public class EntitySubGridSteps : PowerAppsStepDefiner
[When(@"I click the '(.*)' command on the '(.*)' subgrid")]
public static void WhenISelectTheCommandOnTheSubgrid(string commandName, string subGridName)
{
Driver.WaitUntilVisible(
By.CssSelector($"div#dataSetRoot_{subGridName} button[aria-label=\"{commandName}\"]"));
Driver.WaitUntilAvailable(
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subGridName)));

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

/// <summary>
Expand All @@ -234,8 +237,6 @@ public static void ThenICanSeeTheCommandOnTheSubgrid(string commandName, string
[When(@"I click the '([^']+)' flyout on the '([^']+)' subgrid")]
public static void WhenIClickTheFlyoutOnTheSubgrid(string flyoutName, string subGridName)
{
Driver.WaitUntilVisible(By.CssSelector($"div#dataSetRoot_{subGridName} li[aria-label=\"{flyoutName}\"]"));

XrmApp.Entity.SubGrid.ClickCommand(subGridName, flyoutName);
}

Expand All @@ -247,8 +248,11 @@ public static void WhenIClickTheFlyoutOnTheSubgrid(string flyoutName, string sub
[Then(@"I can not see the '(.*)' command on the '(.*)' subgrid")]
public static void ThenICanNotSeeTheCommandOnTheSubgrid(string commandName, string subGridName)
{
Driver.WaitUntilVisible(
By.CssSelector($"div#dataSetRoot_{subGridName} button[aria-label=\"{commandName}\"]"),
Driver
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subGridName)))
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCommandBar]))
.WaitUntilVisible(
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCommandLabel].Replace("[NAME]", commandName)),
new TimeSpan(0, 0, 5))
.Should().BeNull();
}
Expand All @@ -260,10 +264,12 @@ public static void ThenICanNotSeeTheCommandOnTheSubgrid(string commandName, stri
[Then(@"I can see the '(.*)' command on the flyout of the subgrid")]
public static void ThenICanSeeTheCommandOnTheFlyoutOfTheSubgrid(string commandName)
{
Driver.WaitUntilVisible(
By.CssSelector($"#__flyoutRootNode button[aria-label$='{commandName}']"),
new TimeSpan(0, 0, 10),
$"Could not find the {commandName} command on the flyout of the subgrid.");
Driver
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowContainer]))
.WaitUntilVisible(
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", commandName)),
new TimeSpan(0, 0, 10),
$"Could not find the {commandName} command on the flyout of the subgrid.");
}

/// <summary>
Expand All @@ -274,10 +280,12 @@ public static void ThenICanSeeTheCommandOnTheFlyoutOfTheSubgrid(string commandNa
public static void ThenICanNotSeeTheCommandOnTheFlyoutOfTheSubgrid(string commandName)
{
Driver
.Invoking(d => d.WaitUntilVisible(
By.CssSelector($"#__flyoutRootNode button[aria-label$=\"{commandName}\"]"),
new TimeSpan(0, 0, 1),
$"Could not find the {commandName} command on the flyout of the subgrid."))
.Invoking(d => d
.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowContainer]))
.WaitUntilVisible(
By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", commandName)),
new TimeSpan(0, 0, 1),
$"Could not find the {commandName} command on the flyout of the subgrid."))
.Should()
.Throw<Exception>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,6 @@ public static void WhenIPerformASearch(string filterValue)
XrmApp.GlobalSearch.Search(filterValue);
}

/// <summary>
/// Performs an advanced search using the filter attribute and a filter value.
/// </summary>
/// <param name="filterValue">Attribute filter value.</param>
[When("I change the search type using the filter '(.*)'")]
public static void WhenIChangeTheSearchType(string filterValue)
{
XrmApp.GlobalSearch.ChangeSearchType(filterValue);
}

/// <summary>
/// Open a record from a global search at a certain row.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,6 @@
[Binding]
public class LoginSteps : PowerAppsStepDefiner
{
/// <summary>
/// Logs into the given instance with the given credentials.
/// </summary>
/// <param name="driver">WebDriver used to imitate user actions.</param>
/// <param name="orgUrl">The <see cref="Uri"/> of the instance.</param>
/// <param name="username">The username of the user.</param>
/// <param name="password">The password of the user.</param>
public static void Login(IWebDriver driver, Uri orgUrl, string username, string password)
{
driver.Navigate().GoToUrl(orgUrl);
driver.ClickIfVisible(By.Id("otherTile"));

bool waitForMainPage = WaitForMainPage(driver);

if (!waitForMainPage)
{
IWebElement usernameInput = driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Login.UserId]), 30.Seconds());
usernameInput.SendKeys(username);
usernameInput.SendKeys(Keys.Enter);

IWebElement passwordInput = driver.WaitUntilClickable(By.XPath(Elements.Xpath[Reference.Login.LoginPassword]), 30.Seconds());
passwordInput.SendKeys(password);
passwordInput.Submit();

var staySignedIn = driver.WaitUntilClickable(By.XPath(Elements.Xpath[Reference.Login.StaySignedIn]), 10.Seconds());
if (staySignedIn != null)
{
staySignedIn.Click();
}

WaitForMainPage(driver, 30.Seconds());
}
}

/// <summary>
/// Logs in to a given app as a given user.
/// </summary>
Expand All @@ -64,13 +30,16 @@ public static void GivenIAmLoggedInToTheAppAs(string appName, string userAlias)
}

var url = TestConfig.GetTestUrl();
Login(Driver, url, user.Username, user.Password);

XrmApp.OnlineLogin.Login(url, user.Username.ToSecureString(), user.Password.ToSecureString());

if (!url.Query.Contains("appid"))
{
XrmApp.Navigation.OpenApp(appName);
}

Driver.WaitForTransaction();

CloseTeachingBubbles();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static void WhenISelectInTheLookupDialog(string searchTerm)
[When("I click Add in the lookup dialog")]
public static void WhenIClickAddInTheLookupDialog()
{
var container = Driver.WaitUntilAvailable(By.CssSelector("div[id=\"lookupDialogContainer\"]"));
var container = Driver.WaitUntilAvailable(By.CssSelector("div[id=\"lookupDialogFooterContainer\"]"));

container.FindElement(By.CssSelector("button[data-id*=\"lookupDialogSaveBtn\"]"))
.Click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public static void ThenICanSeeTheGroup(string groupName)
var groupNameWithoutWhitespace = groupName?.Replace(" ", string.Empty);

Driver
.WaitUntilAvailable(By.XPath($"//span[@data-id='sitemap-sitemapAreaGroup-{groupNameWithoutWhitespace}']"))
.WaitUntilAvailable(By.XPath($"//h3[@data-id='sitemap-sitemapAreaGroup-{groupNameWithoutWhitespace}']"))
.Text
.Should().Contain(groupName);
}
Expand Down
12 changes: 6 additions & 6 deletions bindings/src/Capgemini.PowerApps.SpecFlowBindings/TestDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,20 @@ public void InjectOnPage(string authToken)
{
var scriptBuilder = new StringBuilder();
scriptBuilder.AppendLine(File.ReadAllText(this.FilePath));
scriptBuilder.AppendLine($@"var recordRepository = new {LibraryNamespace}.CurrentUserRecordRepository(Xrm.WebApi.online);
var metadataRepository = new {LibraryNamespace}.MetadataRepository(Xrm.WebApi.online);
var deepInsertService = new {LibraryNamespace}.DeepInsertService(metadataRepository, recordRepository);");
scriptBuilder.AppendLine($@"var top.recordRepository = new {LibraryNamespace}.CurrentUserRecordRepository(Xrm.WebApi.online);
top.metadataRepository = new {LibraryNamespace}.MetadataRepository(Xrm.WebApi.online);
top.deepInsertService = new {LibraryNamespace}.DeepInsertService(top.metadataRepository, top.recordRepository);");

if (!string.IsNullOrEmpty(authToken))
{
scriptBuilder.AppendLine(
$@"var appUserRecordRepository = new {LibraryNamespace}.AuthenticatedRecordRepository(metadataRepository, '{authToken}');
var dataManager = new {LibraryNamespace}.DataManager(recordRepository, deepInsertService, [new {LibraryNamespace}.FakerPreprocessor()], appUserRecordRepository);");
$@"top.appUserRecordRepository = new {LibraryNamespace}.AuthenticatedRecordRepository(top.metadataRepository, '{authToken}');
top.dataManager = new {LibraryNamespace}.DataManager(top.recordRepository, top.deepInsertService, [new {LibraryNamespace}.FakerPreprocessor()], top.appUserRecordRepository);");
}
else
{
scriptBuilder.AppendLine(
$"var dataManager = new {LibraryNamespace}.DataManager(recordRepository, deepInsertService, [new {LibraryNamespace}.FakerPreprocessor()]);");
$"top.dataManager = new {LibraryNamespace}.DataManager(top.recordRepository, top.deepInsertService, [new {LibraryNamespace}.FakerPreprocessor()]);");
}

scriptBuilder.AppendLine($"{TestDriverReference} = new {LibraryNamespace}.Driver(dataManager);");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<PackageReference Include="Microsoft.CrmSdk.XrmTooling.CoreAssembly" Version="9.1.0.64" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="91.0.4472.10100" />
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="103.0.5060.5300" />
<PackageReference Include="SpecFlow" Version="3.5.14" />
<PackageReference Include="SpecFlow.MsTest" Version="3.5.14" />
<PackageReference Include="SpecFlow.Tools.MsBuild.Generation" Version="3.5.14" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ Scenario: Assert a command not visible
Scenario: Select a command
When I select the 'Refresh' command

@ignore # EasyRepro issue: https://github.com/microsoft/EasyRepro/issues/1087
Scenario: Select a command under a flyout
When I select the 'No Options Available' command under the 'Run Report' flyout
When I select the 'No Options Available' command under the 'Run Report' flyout
Loading