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

fix: get and set multi-select option set values #1041

Merged
merged 1 commit into from
Dec 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,11 @@ public static class MultiSelect
public static string InputSearch = "MultiSelect_InputSearch";
public static string SelectedRecord = "MultiSelect_SelectedRecord";
public static string SelectedRecordButton = "MultiSelect_SelectedRecord_Button";
public static string SelectedOptionDeleteButton = "MultiSelect_SelectedRecord_DeleteButton";
public static string SelectedRecordLabel = "MultiSelect_SelectedRecord_Label";
public static string Flyout = "MultiSelect_Flyout";
public static string FlyoutList = "MultiSelect_FlyoutList";
public static string FlyoutCaret = "MultiSelect_FlyoutCaret";
public static string FlyoutOption = "MultiSelect_FlyoutOption";
public static string FlyoutOptionCheckbox = "MultiSelect_FlyoutOptionCheckbox";
public static string ExpandCollapseButton = "MultiSelect_ExpandCollapseButton";
}

Expand Down Expand Up @@ -514,13 +516,14 @@ public static class AppElements

//MultiSelect
{ "MultiSelect_DivContainer", ".//div[contains(@data-id,\"[NAME]-FieldSectionItemContainer\")]" },
{ "MultiSelect_InputSearch", ".//div[contains(@data-id,\"[NAME].fieldControl-LookupResultsDropdown_[NAME]_InputSearch\")]" },
{ "MultiSelect_SelectedRecord", ".//ul[contains(@data-id,\"[NAME].fieldControl-LookupResultsDropdown_[NAME]_SelectedRecordList\")]//li" },
{ "MultiSelect_SelectedRecord_Button", ".//ul[contains(@data-id,\"[NAME].fieldControl-LookupResultsDropdown_[NAME]\") and contains(@data-id, 'SelectedRecordList')]//li" },
{ "MultiSelect_SelectedRecord_Label", ".//ul[contains(@data-id,\"[NAME].fieldControl-LookupResultsDropdown_[NAME]_SelectedRecordList\")]/descendant::label" },
{ "MultiSelect_Flyout", "//div[contains(@id,\"[NAME].fieldControl|__flyoutRootNode_SimpleLookupControlFlyout\")]//ul" },
{ "MultiSelect_FlyoutList", "//div[contains(@id,\"[NAME].fieldControl|__flyoutRootNode_SimpleLookupControlFlyout\")]//li[descendant::label[contains(text(), \"{0}\")]]" },
{ "MultiSelect_ExpandCollapseButton", ".//button[contains(@data-id,\"[NAME].fieldControl-LookupResultsDropdown_[NAME]_expandCollapse\")]/descendant::label[not(text()=\"+0\")]" },
{ "MultiSelect_InputSearch", ".//input[contains(@class,\"msos-input\")]" },
{ "MultiSelect_SelectedRecord", ".//li[contains(@class, \"msos-selected-display-item\")]" },
{ "MultiSelect_SelectedRecord_DeleteButton", ".//button[contains(@class, \"msos-quick-delete\")]" },
{ "MultiSelect_SelectedRecord_Label", ".//span[contains(@class, \"msos-selected-display-item-text\")]" },
{ "MultiSelect_FlyoutOption", "//li[label[contains(@title, \"[NAME]\")] and contains(@class,\"msos-option\")]" },
{ "MultiSelect_FlyoutOptionCheckbox", "//input[contains(@class, \"msos-checkbox\")]" },
{ "MultiSelect_FlyoutCaret", "//button[contains(@class, \"msos-caret-button\")]" },
{ "MultiSelect_ExpandCollapseButton", ".//button[contains(@class,\"msos-selecteditems-toggle\")]" },

//Dashboard
{ "Dashboard_Selector" , "//span[contains(@id, 'Dashboard_Selector')]"},
Expand Down
20 changes: 10 additions & 10 deletions Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public void ClearValue(MultiValueOptionSet control)
{
_client.ClearValue(control, FormContextType.Entity);
}


/// <summary>
/// Clears a value from the DateTimeControl provided
Expand Down Expand Up @@ -179,7 +179,7 @@ public string GetHeaderValue(OptionSet control)
{
return _client.GetHeaderValue(control);
}

/// <summary>
/// Gets the value of a Boolean Item from the header
/// </summary>
Expand Down Expand Up @@ -208,7 +208,7 @@ public MultiValueOptionSet GetHeaderValue(MultiValueOptionSet control)
{
return _client.GetHeaderValue(control);
}

/// <summary>
/// Gets the value of a DateTime Control from the header
/// </summary>
Expand All @@ -218,7 +218,7 @@ public MultiValueOptionSet GetHeaderValue(MultiValueOptionSet control)
{
return _client.GetHeaderValue(control);
}

/// <summary>
/// Get the object id of the current entity
/// </summary>
Expand All @@ -242,7 +242,7 @@ public string GetFormName()
{
return _client.GetFormName();
}

/// <summary>
/// Get the Header Title of the current entity
/// </summary>
Expand Down Expand Up @@ -281,7 +281,7 @@ public string GetValue(LookupItem control)
return _client.GetValue(control);
}


/// <summary>
/// Gets the value of a Lookup.
/// </summary>
Expand Down Expand Up @@ -333,9 +333,9 @@ public bool GetValue(BooleanItem option)
/// Gets the value of a MultiValueOptionSet.
/// </summary>
/// <param name="option">The option you want to set.</param>
public void GetValue(MultiValueOptionSet option)
public MultiValueOptionSet GetValue(MultiValueOptionSet option)
{
_client.GetValue(option);
return _client.GetValue(option);
}

/// <summary>
Expand Down Expand Up @@ -558,7 +558,7 @@ public void SetValue(MultiValueOptionSet option, bool removeExistingValues = fal
{
_client.SetValue(option, FormContextType.Entity, removeExistingValues);
}

/// <summary>
/// Click Process>Switch Process
/// </summary>
Expand Down Expand Up @@ -597,5 +597,5 @@ public void RemoveValues(LookupItem[] controls)
{
_client.RemoveValues(controls);
}
}
}
}
107 changes: 31 additions & 76 deletions Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2981,33 +2981,21 @@ private BrowserCommandResult<bool> RemoveMultiOptions(MultiValueOptionSet option
fieldContainer = formContext.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.MultiSelect.DivContainer].Replace("[NAME]", option.Name)));
}

// If there are large number of options selected then a small expand collapse
// button needs to be clicked to expose all the list elements.
var xpath = AppElements.Xpath[AppReference.MultiSelect.ExpandCollapseButton].Replace("[NAME]", option.Name);
var expandCollapseButtons = fieldContainer.FindElements(By.XPath(xpath));
if (expandCollapseButtons.Any())
{
expandCollapseButtons.First().Click(true);
}
else
{
// Hover the field to expose the Select Record buttons
fieldContainer.Hover(driver, true);
}

fieldContainer.Hover(driver, true);

xpath = String.Format(AppElements.Xpath[AppReference.MultiSelect.SelectedRecordButton].Replace("[NAME]", option.Name));
var listItemObjects = fieldContainer.FindElements(By.XPath(xpath));
var loopCounts = listItemObjects.Any() ? listItemObjects.Count : 0;
var selectedRecordXPath = By.XPath(AppElements.Xpath[AppReference.MultiSelect.SelectedRecord]);
var selectedRecords = fieldContainer.FindElements(selectedRecordXPath);

for (int i = 0; i < loopCounts; i++)
var initialCountOfSelectedOptions = selectedRecords.Count;
var deleteButtonXpath = By.XPath(AppElements.Xpath[AppReference.MultiSelect.SelectedOptionDeleteButton]);
for (int i = 0; i < initialCountOfSelectedOptions; i++)
{
// With every click of the button, the underlying DOM changes and the
// entire collection becomes stale, hence we only click the first occurance of
// the button and loop back to again find the elements and anyother occurance
listItemObjects[0].FindElement(By.TagName("button")).Click(true);
selectedRecords[0].FindElement(deleteButtonXpath).Click(true);
driver.WaitForTransaction();
listItemObjects = fieldContainer.FindElements(By.XPath(xpath));
selectedRecords = fieldContainer.FindElements(selectedRecordXPath);
}

return true;
Expand Down Expand Up @@ -3057,59 +3045,27 @@ private BrowserCommandResult<bool> AddMultiOptions(MultiValueOptionSet option, F
fieldContainer = formContext.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.MultiSelect.DivContainer].Replace("[NAME]", option.Name)));
}

var inputXPath = By.XPath(AppElements.Xpath[AppReference.MultiSelect.InputSearch]);
fieldContainer.FindElement(inputXPath).SendKeys(string.Empty);

var flyoutCaretXPath = By.XPath(AppElements.Xpath[AppReference.MultiSelect.FlyoutCaret]);
fieldContainer.FindElement(flyoutCaretXPath).Click();

string xpath = AppElements.Xpath[AppReference.MultiSelect.SelectedRecord].Replace("[NAME]", option.Name);
// If there is already some pre-selected items in the div then we must determine if it
// actually exists and simulate a set focus event on that div so that the input textbox
// becomes visible.
var listItems = fieldContainer.FindElements(By.XPath(xpath));
if (listItems.Any())
{
listItems.First().SendKeys("");
}

fieldContainer.ClickWhenAvailable(By.XPath(AppElements.Xpath[AppReference.MultiSelect.InputSearch].Replace("[NAME]", option.Name)));
foreach (var optionValue in option.Values)
{
xpath = String.Format(AppElements.Xpath[AppReference.MultiSelect.FlyoutList].Replace("[NAME]", option.Name), optionValue);
var flyout = fieldContainer.FindElements(By.XPath(xpath));
if (flyout.Any())
var flyoutOptionXPath = By.XPath(AppElements.Xpath[AppReference.MultiSelect.FlyoutOption].Replace("[NAME]", optionValue));
if (fieldContainer.TryFindElement(flyoutOptionXPath, out var flyoutOption))
{
flyout.First().Click(true);
}
else
{
var input = fieldContainer.FindElement(By.TagName("input"));
input.SendKeys(optionValue);
ThinkTime(2000);
var searchFlyout = fieldContainer.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.MultiSelect.Flyout].Replace("[NAME]", option.Name)));
ThinkTime(2000);
var searchResultList = searchFlyout.FindElements(By.TagName("li"));

var ariaSelected = flyoutOption.GetAttribute<string>("aria-selected");
var selected = !string.IsNullOrEmpty(ariaSelected) && bool.Parse(ariaSelected);

// Is the item in search results?
if (searchResultList.Any(x => x.GetAttribute("aria-label").Contains(optionValue, StringComparison.OrdinalIgnoreCase)))
if (!selected)
{
searchResultList.FirstOrDefault(x => x.GetAttribute("aria-label").Contains(optionValue, StringComparison.OrdinalIgnoreCase)).Click(true);
driver.WaitForTransaction();
flyoutOption.Click();
}
}
}

// Click on the div containing textbox so that the floyout collapses or else the flyout
// will interfere in finding the next multiselect control which by chance will be lying
// behind the flyout control.
//driver.ClickWhenAvailable(By.XPath(AppElements.Xpath[AppReference.MultiSelect.DivContainer].Replace("[NAME]", Elements.ElementId[option.Name])));
xpath = AppElements.Xpath[AppReference.MultiSelect.DivContainer].Replace("[NAME]", option.Name);
var divElements = fieldContainer.FindElements(By.XPath(xpath));
if (divElements.Any())
{
divElements.First().Click(true);
}

fieldContainer.Click(true);

return true;
});
}
Expand Down Expand Up @@ -3367,25 +3323,24 @@ internal BrowserCommandResult<MultiValueOptionSet> GetValue(MultiValueOptionSet
{
return this.Execute(GetOptions($"Get Multi Select Value: {option.Name}"), driver =>
{
// If there are large number of options selected then a small expand collapse
// button needs to be clicked to expose all the list elements.
string xpath = AppElements.Xpath[AppReference.MultiSelect.ExpandCollapseButton].Replace("[NAME]", Elements.ElementId[option.Name]);
var expandCollapseButtons = driver.FindElements(By.XPath(xpath));
if (expandCollapseButtons.Any())
var containerXPath = By.XPath(AppElements.Xpath[AppReference.MultiSelect.DivContainer].Replace("[NAME]", option.Name));
var container = driver.WaitUntilAvailable(containerXPath, $"Multi-select option set {option.Name} not found.");

container.Hover(driver, true);
var expandButtonXPath = By.XPath(AppElements.Xpath[AppReference.MultiSelect.ExpandCollapseButton]);
if (container.TryFindElement(expandButtonXPath, out var expandButton) && expandButton.IsClickable())
{
expandCollapseButtons.First().Click(true);
expandButton.Click();
}

var returnValue = new MultiValueOptionSet { Name = option.Name };
var selectedOptionsXPath = By.XPath(AppElements.Xpath[AppReference.MultiSelect.SelectedRecordLabel]);
var selectedOptions = container.FindElements(selectedOptionsXPath);

xpath = AppElements.Xpath[AppReference.MultiSelect.SelectedRecordLabel].Replace("[NAME]", Elements.ElementId[option.Name]);
var labelItems = driver.FindElements(By.XPath(xpath));
if (labelItems.Any())
return new MultiValueOptionSet
{
returnValue.Values = labelItems.Select(x => x.Text).ToArray();
}

return returnValue;
Name = option.Name,
Values = selectedOptions.Select(o => o.Text).ToArray()
};
});
}

Expand Down