diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Controls/CrmControls.cs b/Microsoft.Dynamics365.UIAutomation.Api/Controls/CrmControls.cs index 7561bc6d..b5f7dd7a 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Controls/CrmControls.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Controls/CrmControls.cs @@ -51,6 +51,12 @@ public class OptionSet public string Value { get; set; } } + public class MultiValueOptionSet + { + public string Name { get; set; } + public string[] Values { get; set; } + } + public class Lookup { public string Name { get; set; } diff --git a/Microsoft.Dynamics365.UIAutomation.Api/DTOs/XrmElementReference.cs b/Microsoft.Dynamics365.UIAutomation.Api/DTOs/XrmElementReference.cs index cc48eee1..35c1ba0f 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/DTOs/XrmElementReference.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/DTOs/XrmElementReference.cs @@ -42,6 +42,8 @@ public static class Elements { "Dialog_AddConnectionSave", "id(\"connection|NoRelationship|Form|Mscrm.Form.connection.SaveAndClose-Large\")" }, { "Dialog_RoleLookupButton", "id(\"record2roleid\")" }, { "Dialog_RoleLookupTable", "id(\"record2roleid_IMenu\")" }, + { "Dialog_WarningFooter" , "//*[@id=\"crmDialogFooter\"]" }, + { "Dialog_WarningCloseButton", "//*[@id=\"butBegin\"]" }, //GuidedHelp { "GuidedHelp_MarsOverlay" , "id(\"marsOverlay\")"}, @@ -152,10 +154,8 @@ public static class Elements { "Notes_ActivityAddTaskDueTime" ,"id(\"selectTable_Date\")"}, //Login - { "Login_UserId", "id(\"i0116\")"}, - { "Login_Password", "id(\"i0118\")"}, - { "Login_OldUserId", "id(\"cred_userid_inputtext\")"}, - { "Login_OldPassword", "id(\"cred_password_inputtext\")"}, + { "Login_UserId", "//input[@type='email']"}, + { "Login_Password", "//input[@type='password']"}, { "Login_SignIn", "id(\"cred_sign_in_button\")"}, { "Login_CrmMainPage", "id(\"crmTopBar\")"}, { "Login_StaySignedIn", "id(\"idSIButton9\")"}, @@ -279,7 +279,9 @@ public static class Elements //SetValue { "SetValue_LookupRenderClass" , "Lookup_RenderButton_td"}, { "SetValue_EditClass" , "ms-crm-Inline-Edit"}, - { "SetValue_ValueClass" , "ms-crm-Inline-Value"}, + { "SetValue_ValueClass" , "ms-crm-Inline-Value"}, + { "SetValue_MultiSelectPicklistDelete" , "sol-quick-delete"}, + //DashBoard { "DashBoard_ViewContainerClass" , "ms-crm-VS-Menu"}, @@ -337,6 +339,8 @@ public static class Dialogs public static string WorkflowHeader = "Dialog_WorkflowHeader"; public static string ProcessFlowHeader = "Dialog_ProcessFlowHeader"; public static string AddConnectionHeader = "Dialog_AddConnectionHeader"; + public static string WarningFooter = "Dialog_WarningFooter"; + public static string WarningCloseButton = "Dialog_WarningCloseButton"; public static class CloseOpportunity { @@ -404,6 +408,7 @@ public static class SetValue public static string CompositionLinkControl = "SetValue_CompositionLinkControlId"; public static string Cancel = "SetValue_Cancel"; public static string Save = "SetValue_Save"; + public static string MultiSelectPicklistDeleteClass = "SetValue_MultiSelectPicklistDelete"; } public static class QuickCreate @@ -609,9 +614,7 @@ public static class LookUp public static class Login { public static string UserId = "Login_UserId"; - public static string Password = "Login_Password"; - public static string OldSignInUserId = "Login_OldUserId"; - public static string OldSignInPassword = "Login_OldPassword"; + public static string LoginPassword = "Login_Password"; public static string SignIn = "Login_SignIn"; public static string CrmMainPage = "Login_CrmMainPage"; public static string StaySignedIn = "Login_StaySignedIn"; diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Microsoft.Dynamics365.UIAutomation.Api.csproj b/Microsoft.Dynamics365.UIAutomation.Api/Microsoft.Dynamics365.UIAutomation.Api.csproj index b77a49f4..8b6be269 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Microsoft.Dynamics365.UIAutomation.Api.csproj +++ b/Microsoft.Dynamics365.UIAutomation.Api/Microsoft.Dynamics365.UIAutomation.Api.csproj @@ -25,7 +25,7 @@ pdbonly true - \bin\Release\ + bin\Release\ TRACE prompt 4 diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/LoginPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/LoginPage.cs index 47e62b4a..9da4240f 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/LoginPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/LoginPage.cs @@ -88,7 +88,6 @@ private LoginResult Login(IWebDriver driver, Uri uri, SecureString username, Sec { var redirect = false; bool online = !(this.OnlineDomains != null && !this.OnlineDomains.Any(d => uri.Host.EndsWith(d))); - bool redirectToNewPassword = false; driver.Navigate().GoToUrl(uri); if (online) @@ -96,25 +95,13 @@ private LoginResult Login(IWebDriver driver, Uri uri, SecureString username, Sec if (driver.IsVisible(By.Id("use_another_account_link"))) driver.ClickWhenAvailable(By.Id("use_another_account_link")); - if (driver.IsVisible(By.XPath(Elements.Xpath[Reference.Login.UserId]))) - { driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Login.UserId]), $"The Office 365 sign in page did not return the expected result and the user '{username}' could not be signed in."); driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.UserId])).SendKeys(username.ToUnsecureString()); driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.UserId])).SendKeys(Keys.Tab); driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.UserId])).SendKeys(Keys.Enter); - redirectToNewPassword = true; Thread.Sleep(2000); - } - else - { - driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Login.OldSignInUserId]), - $"The Office 365 sign in page did not return the expected result and the user '{username}' could not be signed in."); - - driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.OldSignInUserId])).SendKeys(username.ToUnsecureString()); - driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.OldSignInUserId])).SendKeys(Keys.Tab); - } //If expecting redirect then wait for redirect to trigger if (redirectAction != null) @@ -128,24 +115,15 @@ private LoginResult Login(IWebDriver driver, Uri uri, SecureString username, Sec } else { - if (redirectToNewPassword) - { - driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.Password])).SendKeys(password.ToUnsecureString()); - driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.Password])).SendKeys(Keys.Tab); - driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.Password])).Submit(); + driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.LoginPassword])).SendKeys(password.ToUnsecureString()); + driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.LoginPassword])).SendKeys(Keys.Tab); + driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.LoginPassword])).Submit(); if (driver.IsVisible(By.XPath(Elements.Xpath[Reference.Login.StaySignedIn]))) { driver.ClickWhenAvailable(By.XPath(Elements.Xpath[Reference.Login.StaySignedIn])); driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.StaySignedIn])).Submit(); } - } - else - { - driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.OldSignInPassword])).SendKeys(password.ToUnsecureString()); - driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.OldSignInPassword])).SendKeys(Keys.Tab); - driver.FindElement(By.XPath(Elements.Xpath[Reference.Login.OldSignInPassword])).Submit(); - } driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]) , new TimeSpan(0, 0, 60), diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmActivityFeedPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmActivityFeedPage.cs index 277935c6..cf8eed31 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmActivityFeedPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmActivityFeedPage.cs @@ -232,11 +232,10 @@ public BrowserCommandResult AddTask(string subject, string description, Da this.SetValue(Elements.ElementId[Reference.ActivityFeed.ActivityTaskSubjectId], subject); this.SetValue(Elements.ElementId[Reference.ActivityFeed.ActivityTaskDescriptionId], description); - this.SetCalenderValue(Elements.ElementId[Reference.ActivityFeed.ActivityAddTaskDueDateId], dueDate.ToShortDateString()); - driver.ClickWhenAvailable(By.XPath(Elements.Xpath[Reference.ActivityFeed.ActivityTaskScheduledEnd])); this.SetCalenderValue(Elements.ElementId[Reference.ActivityFeed.ActivityAddTaskDueTimeId], dueDate.ToShortTimeString()); this.SetValue(priority); + driver.ClickWhenAvailable(By.XPath(Elements.Xpath[Reference.ActivityFeed.ActivityTaskOk])); return true; diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmCommandBarPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmCommandBarPage.cs index 5020b449..d45d3603 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmCommandBarPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmCommandBarPage.cs @@ -2,10 +2,6 @@ // Licensed under the MIT license. using Microsoft.Dynamics365.UIAutomation.Browser; -using OpenQA.Selenium; -using System; -using System.Collections.ObjectModel; -using System.Linq; namespace Microsoft.Dynamics365.UIAutomation.Api { @@ -30,48 +26,5 @@ public XrmCommandBarPage(InteractiveBrowser browser) { SwitchToDefault(); } - - - /// - /// Gets the Commands - /// - /// The MoreCommands - /// - private BrowserCommandResult> GetCommands(bool moreCommands = false) - { - return this.Execute("Get Command Bar Buttons", driver => - { - driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.CommandBar.RibbonManager]),new TimeSpan(0,0,5)); - - IWebElement ribbon = null; - if (moreCommands) - ribbon = driver.FindElement(By.XPath(Elements.Xpath[Reference.CommandBar.List])); - else - ribbon = driver.FindElement(By.XPath(Elements.Xpath[Reference.CommandBar.RibbonManager])); - - var items = ribbon.FindElements(By.TagName("li")); - - return items;//.Where(item => item.Text.Length > 0).ToDictionary(item => item.Text, item => item.GetAttribute("id")); - }); - } - - /// - /// Clicks the Command - /// - /// The Name of the command - /// The SubName - /// The MoreCommands - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - /// xrmBrowser.CommandBar.ClickCommand("New"); - public BrowserCommandResult ClickCommand(string name, string subName = "", bool moreCommands = false, int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("Click Command"), driver => - { - ClickCommandButton(name, subName, moreCommands, thinkTime); - return true; - }); - } } } diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmDialogPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmDialogPage.cs index 3978ed54..68844235 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmDialogPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmDialogPage.cs @@ -288,5 +288,22 @@ public BrowserCommandResult AddUser(int thinkTime = Constants.DefaultThink }); } + public BrowserCommandResult CloseWarningDialog() + { + return this.Execute(GetOptions($"Close Warning Dialog"), driver => + { + var dialogFooter = driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Dialogs.WarningFooter])); + + if (dialogFooter != null + && (dialogFooter?.FindElements(By.XPath(Elements.Xpath[Reference.Dialogs.WarningCloseButton]))).Any()) + { + var closeBtn = + dialogFooter.FindElement(By.XPath(Elements.Xpath[Reference.Dialogs.WarningCloseButton])); + closeBtn.Click(); + } + + return true; + }); + } } } diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmEntityPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmEntityPage.cs index 3523b218..239dfb41 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmEntityPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmEntityPage.cs @@ -4,7 +4,6 @@ using Microsoft.Dynamics365.UIAutomation.Browser; using OpenQA.Selenium; using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; @@ -228,145 +227,6 @@ public BrowserCommandResult ExpandTab(string name, int thinkTime = Constan }); } - /// - /// Set Lookup Value for the field - /// - /// The Field - /// The Index - /// xrmBrowser.Entity.SelectLookup("customerid", 0); - public BrowserCommandResult SelectLookup(string field, [Range(0, 9)]int index) - { - return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver => - { - if (driver.HasElement(By.Id(field))) - { - var input = driver.ClickWhenAvailable(By.Id(field)); - - if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) - throw new InvalidOperationException($"Field: {field} is not lookup"); - - input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])).Click(); - - var dialogName = $"Dialog_{field}_IMenu"; - var dialog = driver.FindElement(By.Id(dialogName)); - - var dialogItems = OpenDialog(dialog).Value; - - if (dialogItems.Count < index) - throw new InvalidOperationException($"List does not have {index + 1} items."); - - var dialogItem = dialogItems[index]; - dialogItem.Element.Click(); - } - else - throw new InvalidOperationException($"Field: {field} Does not exist"); - - return true; - }); - } - - /// - /// Set Lookup Value for the field - /// - /// The Field - /// The Lookup value - public BrowserCommandResult SelectLookup(string field, string value) - { - return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver=> - { - if (driver.HasElement(By.Id(field))) - { - var input = driver.ClickWhenAvailable(By.Id(field)); - - if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) - throw new InvalidOperationException($"Field: {field} is not lookup"); - - var lookupIcon = input.FindElement(By.ClassName("Lookup_RenderButton_td")); - lookupIcon.Click(); - - var dialogName = $"Dialog_{field}_IMenu"; - var dialog = driver.FindElement(By.Id(dialogName)); - - var dialogItems = OpenDialog(dialog).Value; - - if (!dialogItems.Exists(x => x.Title == value)) - throw new InvalidOperationException($"List does not have {value}."); - - var dialogItem = dialogItems.Where(x => x.Title == value).First(); - dialogItem.Element.Click(); - } - else - throw new InvalidOperationException($"Field: {field} Does not exist"); - - return true; - }); - } - - /// - /// Set Lookup Value for the field - /// - /// The Field - /// The Open Lookup Page - public BrowserCommandResult SelectLookup(string field, bool openLookupPage = true) - { - return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver=> - { - if (driver.HasElement(By.Id(field))) - { - var input = driver.ClickWhenAvailable(By.Id(field)); - - if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) - throw new InvalidOperationException($"Field: {field} is not lookup"); - - input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])).Click(); - - var dialogName = $"Dialog_{field}_IMenu"; - var dialog = driver.FindElement(By.Id(dialogName)); - - var dialogItems = OpenDialog(dialog).Value; - - if (dialogItems.Any()) - { - var dialogItem = dialogItems.Last(); - dialogItem.Element.Click(); - } - } - else - throw new InvalidOperationException($"Field: {field} Does not exist"); - - return true; - }); - } - - /// - /// Opens the dialog - /// - /// - private BrowserCommandResult> OpenDialog(IWebElement dialog) - { - var list = new List(); - var dialogItems = dialog.FindElements(By.TagName("li")); - - foreach (var dialogItem in dialogItems) - { - if (dialogItem.GetAttribute("role") != null && dialogItem.GetAttribute("role") == "menuitem") - { - var links = dialogItem.FindElements(By.TagName("a")); - - if (links != null && links.Count > 1) - { - var title = links[1].GetAttribute("title"); - - list.Add(new XrmListItem() { - Title = title, - Element = links[1] }); - } - } - } - - return list; - } - /// /// Popout the form /// diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmGridPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmGridPage.cs index d7aa00a7..0d8f4d42 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmGridPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmGridPage.cs @@ -124,107 +124,6 @@ public BrowserCommandResult SwitchView(string viewName, int thinkTime = Co }); } - /// - /// Refreshes this instance. - /// - /// - public BrowserCommandResult Refresh(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("Refresh"), driver => - { - driver.ClickWhenAvailable(By.XPath(Elements.Xpath[Reference.Grid.Refresh])); - - return true; - }); - } - - /// - /// Firsts the page. - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - /// xrmBrowser.Grid.FirstPage(); - public BrowserCommandResult FirstPage(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("FirstPage"), driver => - { - var firstPageIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.FirstPage])); - - if (firstPageIcon.GetAttribute("disabled") != null) - return false; - else - firstPageIcon.Click(); - return true; - }); - } - - /// - /// Nexts the page. - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - /// xrmBrowser.Grid.NextPage(); - public BrowserCommandResult NextPage(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("Next"), driver => - { - var nextIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.NextPage])); - - if (nextIcon.GetAttribute("disabled") != null) - return false; - else - nextIcon.Click(); - return true; - }); - } - - /// - /// Toggles the select all. - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - /// xrmBrowser.Grid.SelectAllRecords(); - public BrowserCommandResult SelectAllRecords(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("ToggleSelectAll"), driver => - { - // We can check if any record selected by using - // driver.FindElements(By.ClassName("ms-crm-List-SelectedRow")).Count == 0 - // but this function doesn't check it. - var selectAll = driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Grid.ToggleSelectAll]), - "The Toggle SelectAll is not available."); - - selectAll.Click(); - - return true; - }); - } - - /// - /// Previouses the page. - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - /// xrmBrowser.Grid.PreviousPage(); - public BrowserCommandResult PreviousPage(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("PreviousPage"), driver => - { - var previousIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.PreviousPage])); - - if (previousIcon.GetAttribute("disabled") != null) - return false; - else - previousIcon.Click(); - return true; - }); - } /// /// Opens the chart. @@ -319,61 +218,6 @@ public BrowserCommandResult Sort(string columnName,int thinkTime = Constan }); } - /// - /// Gets the grid items. - /// - /// xrmBrowser.Grid.GetGridItems(); - public BrowserCommandResult> GetGridItems() - { - return this.Execute(GetOptions("Get Grid Items"), driver => - { - var returnList = new List(); - - var itemsTable = driver.FindElement(By.XPath(@"//*[@id=""gridBodyTable""]/tbody")); - var columnGroup = driver.FindElement(By.XPath(@"//*[@id=""gridBodyTable""]/colgroup")); - - var rows = itemsTable.FindElements(By.TagName("tr")); - - foreach (var row in rows) - { - if (!string.IsNullOrEmpty(row.GetAttribute("oid"))) - { - Guid id = Guid.Parse(row.GetAttribute("oid")); - var link = - $"{new Uri(driver.Url).Scheme}://{new Uri(driver.Url).Authority}/main.aspx?etn={row.GetAttribute("otypename")}&pagetype=entityrecord&id=%7B{id:D}%7D"; - - var item = new XrmGridItem - { - EntityName = row.GetAttribute("otypename"), - Id = id, - Url = new Uri(link) - }; - - var cells = row.FindElements(By.TagName("td")); - var idx = 0; - - foreach (var column in columnGroup.FindElements(By.TagName("col"))) - { - var name = column.GetAttribute("name"); - - if (!string.IsNullOrEmpty(name) - && column.GetAttribute("class").Contains(Elements.CssClass[Reference.Grid.DataColumn]) - && cells.Count > idx) - { - item[name] = cells[idx].Text; - } - - idx++; - } - - returnList.Add(item); - } - } - - return returnList; - }); - } - /// /// Opens the grid record. /// diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmNavigationPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmNavigationPage.cs index 8cc1e701..26dcd054 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmNavigationPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmNavigationPage.cs @@ -60,7 +60,7 @@ public BrowserCommandResult OpenHomePage(int thinkTime = Constants.Default foreach (var subItem in subItems) { - dictionary.Add(subItem.Text.ToLowerString(), subItem); + dictionary.Add(subItem.GetAttribute("title").ToLowerString(), subItem); } return dictionary; @@ -163,7 +163,8 @@ public BrowserCommandResult GlobalSearch(string searchText, int thinkTime d => { driver.FindElement(By.XPath(Elements.Xpath[Reference.Navigation.Search])).SendKeys(searchText, true); - driver.ClickWhenAvailable(By.XPath(Elements.Xpath[Reference.Navigation.StartSearch])); + Thread.Sleep(500); + driver.FindElement(By.XPath(Elements.Xpath[Reference.Navigation.Search])).SendKeys(Keys.Enter); }, d => { throw new InvalidOperationException("The Global Search text field is not available."); }); diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmQuickCreatePage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmQuickCreatePage.cs index b58cd394..b8304785 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmQuickCreatePage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmQuickCreatePage.cs @@ -4,8 +4,6 @@ using Microsoft.Dynamics365.UIAutomation.Browser; using OpenQA.Selenium; using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Linq; namespace Microsoft.Dynamics365.UIAutomation.Api @@ -63,144 +61,6 @@ public BrowserCommandResult Save(int thinkTime = Constants.DefaultThinkTim }); } - /// - /// Set Lookup Value for the field - /// - /// The Field Value - /// The Index - /// xrmBrowser.QuickCreate.SelectLookup("primarycontactid", 0); - public BrowserCommandResult SelectLookup(string field, [Range(0, 9)]int index) - { - return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver => - { - if (driver.HasElement(By.Id(field))) - { - var input = driver.ClickWhenAvailable(By.Id(field)); - - if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) - throw new InvalidOperationException($"Field: {field} is not lookup"); - - input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])).Click(); - - var dialogName = $"Dialog_{field}_IMenu"; - var dialog = driver.FindElement(By.Id(dialogName)); - - var dialogItems = OpenDialog(dialog).Value; - - if (dialogItems.Count < index) - throw new InvalidOperationException($"List does not have {index + 1} items."); - - var dialogItem = dialogItems.Values.ToList()[index]; - dialogItem.Click(); - } - else - throw new InvalidOperationException($"Field: {field} Does not exist"); - - return true; - }); - } - - /// - /// Set Lookup Value for the field - /// - /// The Field - /// The Value - public BrowserCommandResult SelectLookup(string field, string value) - { - return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver => - { - if (driver.HasElement(By.Id(field))) - { - var input = driver.ClickWhenAvailable(By.Id(field)); - - if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) - throw new InvalidOperationException($"Field: {field} is not lookup"); - - var lookupIcon = input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])); - lookupIcon.Click(); - - var dialogName = $"Dialog_{field}_IMenu"; - var dialog = driver.FindElement(By.Id(dialogName)); - - var dialogItems = OpenDialog(dialog).Value; - - if (!dialogItems.Keys.Contains(value)) - throw new InvalidOperationException($"List does not have {value}."); - - var dialogItem = dialogItems[value]; - dialogItem.Click(); - } - else - throw new InvalidOperationException($"Field: {field} Does not exist"); - - return true; - }); - } - - /// - /// Set Lookup Value - /// - /// The Field - /// The Open Lookup Page - public BrowserCommandResult SelectLookup(string field, bool openLookupPage = true) - { - return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver => - { - if (driver.HasElement(By.Id(field))) - { - var input = driver.ClickWhenAvailable(By.Id(field)); - - if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) - throw new InvalidOperationException($"Field: {field} is not lookup"); - - input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])).Click(); - - var dialogName = $"Dialog_{field}_IMenu"; - var dialog = driver.FindElement(By.Id(dialogName)); - - var dialogItems = OpenDialog(dialog).Value; - - var dialogItem = dialogItems.Values.Last(); - - try - { - dialogItem.Click(); - } - catch (OpenQA.Selenium.StaleElementReferenceException) - { - // Expected error - } - } - else - throw new InvalidOperationException($"Field: {field} Does not exist"); - - return true; - }); - } - - private BrowserCommandResult> OpenDialog(IWebElement dialog) - { - var dictionary = new Dictionary(); - var dialogItems = dialog.FindElements(By.TagName("li")); - - foreach (var dialogItem in dialogItems) - { - if (dialogItem.GetAttribute("role") != null && dialogItem.GetAttribute("role") == "menuitem") - { - var links = dialogItem.FindElements(By.TagName("a")); - - if (links != null && links.Count > 1) - { - var title = links[1].GetAttribute("title"); - - dictionary.Add(title, links[1]); - } - } - } - - return dictionary; - } - /// /// Sets the value of a Checkbox field. /// diff --git a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmRelatedGridPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmRelatedGridPage.cs index 7c72b080..b5735176 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmRelatedGridPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/Pages/XrmRelatedGridPage.cs @@ -5,7 +5,6 @@ using OpenQA.Selenium; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Linq; using System.Threading; @@ -109,103 +108,6 @@ public BrowserCommandResult SwitchView(string viewName, int thinkTime = Co }); } - /// - /// Refresh - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - public BrowserCommandResult Refresh(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("Refresh"), driver => - { - driver.ClickWhenAvailable(By.XPath(Elements.Xpath[Reference.Grid.Refresh])); - - return true; - }); - } - - /// - /// Firsts the page. - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - public BrowserCommandResult FirstPage(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("FirstPage"), driver => - { - var firstPageIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.FirstPage])); - - if (firstPageIcon.GetAttribute("disabled") != null) - return false; - else - firstPageIcon.Click(); - return true; - }); - } - - /// - /// Nexts the page. - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - public BrowserCommandResult NextPage(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("Next"), driver => - { - var nextIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.NextPage])); - - if (nextIcon.GetAttribute("disabled") != null) - return false; - else - nextIcon.Click(); - return true; - }); - } - - /// - /// Toggles the select all. - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - public BrowserCommandResult SelectAllRecords(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("ToggleSelectAll"), driver => - { - // We can check if any record selected by using - // driver.FindElements(By.ClassName("ms-crm-List-SelectedRow")).Count == 0 - // but this function doesn't check it. - var selectAll = driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Grid.ToggleSelectAll]), - "The Toggle SelectAll is not available."); - - selectAll.Click(); - - return true; - }); - } - - /// - /// Previouses the page. - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - public BrowserCommandResult PreviousPage(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("PreviousPage"), driver => - { - var previousIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.PreviousPage])); - - if (previousIcon.GetAttribute("disabled") != null) - return false; - else - previousIcon.Click(); - return true; - }); - } /// /// Searches the specified search criteria. @@ -252,63 +154,6 @@ public BrowserCommandResult Sort(string columnName, int thinkTime = Consta }); } - /// - /// Get the Grid Items - /// - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - public BrowserCommandResult> GetGridItems(int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("Get Grid Items"), driver => - { - var returnList = new List(); - - var itemsTable = driver.FindElement(By.XPath(@"//*[@id=""gridBodyTable""]/tbody")); - var columnGroup = driver.FindElement(By.XPath(@"//*[@id=""gridBodyTable""]/colgroup")); - - var rows = itemsTable.FindElements(By.TagName("tr")); - - foreach (var row in rows) - { - if (!string.IsNullOrEmpty(row.GetAttribute("oid"))) - { - Guid id = Guid.Parse(row.GetAttribute("oid")); - var link = - $"{new Uri(driver.Url).Scheme}://{new Uri(driver.Url).Authority}/main.aspx?etn={row.GetAttribute("otypename")}&pagetype=entityrecord&id=%7B{id:D}%7D"; - - var item = new XrmGridItem - { - EntityName = row.GetAttribute("otypename"), - Id = id, - Url = new Uri(link) - }; - - var cells = row.FindElements(By.TagName("td")); - var idx = 0; - - foreach (var column in columnGroup.FindElements(By.TagName("col"))) - { - var name = column.GetAttribute("name"); - - if (!string.IsNullOrEmpty(name) - && column.GetAttribute("class").Contains(Elements.CssClass[Reference.Grid.DataColumn]) - && cells.Count > idx) - { - item[name] = cells[idx].Text; - } - - idx++; - } - - returnList.Add(item); - } - } - - return returnList; - }); - } - /// /// Opens the grid record. /// @@ -356,24 +201,5 @@ public BrowserCommandResult OpenGridRow(int index, int thinkTime = Constan } }); } - - /// - /// Clicks the Command - /// - /// The name - /// The subName - /// The moreCommands - /// Used to simulate a wait time between human interactions. The Default is 2 seconds. - /// xrmBrowser.Related.ClickCommand("ADD NEW CASE"); - public BrowserCommandResult ClickCommand(string name, string subName = "", bool moreCommands = false, int thinkTime = Constants.DefaultThinkTime) - { - Browser.ThinkTime(thinkTime); - - return this.Execute(GetOptions("Click Command"), driver => - { - ClickCommandButton(name, subName, moreCommands, thinkTime); - return true; - }); - } } } \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Api/XrmPage.cs b/Microsoft.Dynamics365.UIAutomation.Api/XrmPage.cs index ff430152..0f00724d 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api/XrmPage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api/XrmPage.cs @@ -6,9 +6,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; using System.Linq; - namespace Microsoft.Dynamics365.UIAutomation.Api { @@ -37,12 +37,16 @@ public BrowserCommandResult SetValue(string field, bool check) //return this.Execute($"Set Value: {field}", SetValue, field, check); return this.Execute(GetOptions($"Set Value: {field}"), driver => { - if (driver.HasElement(By.Id("int_" + field))) + if (driver.HasElement(By.Id(field))) { - var input = driver.FindElement(By.Id("int_" + field)); + var input = driver.FindElement(By.Id(field)); + var checkBox = input.FindElement(By.TagName("input")); + var bCheck = checkBox.GetAttribute("value") == "1"; - if (bool.Parse(input.FindElement(By.TagName("input")).GetAttribute("checked")) && !check) - input.FindElement(By.TagName("input")).Click(); + if (bCheck != check) + { + checkBox.Click(); + } } else throw new InvalidOperationException($"Field: {field} Does not exist"); @@ -227,6 +231,52 @@ public BrowserCommandResult SetValue(OptionSet option) }); } + /// + /// Sets the value of a multi-value picklist. + /// + /// The option you want to set. + /// xrmBrowser.Entity.SetValue(new OptionSet { Name = "preferredcontactmethodcode", Value = "Email" }); + public BrowserCommandResult SetValue(MultiValueOptionSet option, bool removeExistingValues = false) + { + return this.Execute(GetOptions($"Set Value: {option.Name}"), driver => + { + driver.WaitUntilVisible(By.Id(option.Name)); + + if (driver.HasElement(By.Id(option.Name))) + { + var container = driver.ClickWhenAvailable(By.Id(option.Name)); + + if(removeExistingValues) + { + //Remove Existing Values + var values = container.FindElements(By.ClassName(Elements.CssClass[Reference.SetValue.MultiSelectPicklistDeleteClass])); + foreach (var value in values) + value.Click(true); + } + + var input = container.FindElement(By.TagName("input")); + input.Click(); + input.SendKeys(" "); + + var options = container.FindElements(By.TagName("li")); + + foreach (var op in options) + { + var label = op.FindElement(By.TagName("label")); + + if (option.Values.Contains(op.Text) || option.Values.Contains(op.GetAttribute("value")) || option.Values.Contains(label.GetAttribute("title"))) + op.Click(true); + } + + container.Click(); + } + else + throw new InvalidOperationException($"Field: {option.Name} Does not exist"); + + return true; + }); + } + /// /// Sets the value of a Composite control. /// @@ -295,21 +345,24 @@ public BrowserCommandResult SetValue(Lookup control) var dialogItems = OpenDialog(dialog).Value; - if(control.Value != null) + + if (control.Value != null) { - if (!dialogItems.Keys.Contains(control.Value)) + + if (!dialogItems.Exists(x => x.Title == control.Value)) throw new InvalidOperationException($"List does not have {control.Value}."); - var dialogItem = dialogItems[control.Value]; - dialogItem.Click(); + var dialogItem = dialogItems.Where(x => x.Title == control.Value).First(); + dialogItem.Element.Click(); + } else { if (dialogItems.Count < control.Index) throw new InvalidOperationException($"List does not have {control.Index + 1} items."); - var dialogItem = dialogItems.Values.ToList()[control.Index]; - dialogItem.Click(); + var dialogItem = dialogItems[control.Index]; + dialogItem.Element.Click(); } } else @@ -656,43 +709,17 @@ internal BrowserCommandOptions GetOptions(string commandName) typeof(NoSuchElementException), typeof(StaleElementReferenceException)); } - - /// - /// Open Dialog - /// - /// The dialog - /// - private BrowserCommandResult> OpenDialog(IWebElement dialog) - { - var dictionary = new Dictionary(); - var dialogItems = dialog.FindElements(By.TagName("li")); - - foreach (var dialogItem in dialogItems) - { - if (dialogItem.GetAttribute("role") != null && dialogItem.GetAttribute("role") == "menuitem") - { - var links = dialogItem.FindElements(By.TagName("a")); - - if (links != null && links.Count > 1) - { - var title = links[1].GetAttribute("title"); - - dictionary.Add(title, links[1]); - } - } - } - return dictionary; - } - /// /// Gets the Commands /// /// The MoreCommands /// - private BrowserCommandResult> GetCommands(bool moreCommands = false) + public BrowserCommandResult> GetCommands(bool moreCommands = false) { return this.Execute(GetOptions("Get Command Bar Buttons"), driver => { + driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.CommandBar.RibbonManager]), new TimeSpan(0, 0, 5)); + IWebElement ribbon = null; if (moreCommands) ribbon = driver.FindElement(By.XPath(Elements.Xpath[Reference.CommandBar.List])); @@ -753,5 +780,325 @@ internal bool ClickCommandButton(string name, string subName = "", bool moreComm } + /// + /// Clicks the Command + /// + /// The name + /// The subName + /// The moreCommands + /// Used to simulate a wait time between human interactions. The Default is 2 seconds. + /// xrmBrowser.Related.ClickCommand("ADD NEW CASE"); + public BrowserCommandResult ClickCommand(string name, string subName = "", bool moreCommands = false, int thinkTime = Constants.DefaultThinkTime) + { + Browser.ThinkTime(thinkTime); + + return this.Execute(GetOptions("Click Command"), driver => + { + ClickCommandButton(name, subName, moreCommands, thinkTime); + return true; + }); + } + + /// + /// Refreshes this instance. + /// + /// + public BrowserCommandResult Refresh(int thinkTime = Constants.DefaultThinkTime) + { + Browser.ThinkTime(thinkTime); + + return this.Execute(GetOptions("Refresh"), driver => + { + driver.ClickWhenAvailable(By.XPath(Elements.Xpath[Reference.Grid.Refresh])); + + return true; + }); + } + + /// + /// Firsts the page. + /// + /// Used to simulate a wait time between human interactions. The Default is 2 seconds. + /// xrmBrowser.Grid.FirstPage(); + public BrowserCommandResult FirstPage(int thinkTime = Constants.DefaultThinkTime) + { + Browser.ThinkTime(thinkTime); + + return this.Execute(GetOptions("FirstPage"), driver => + { + var firstPageIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.FirstPage])); + + if (firstPageIcon.GetAttribute("disabled") != null) + return false; + else + firstPageIcon.Click(); + return true; + }); + } + + /// + /// Nexts the page. + /// + /// Used to simulate a wait time between human interactions. The Default is 2 seconds. + /// xrmBrowser.Grid.NextPage(); + public BrowserCommandResult NextPage(int thinkTime = Constants.DefaultThinkTime) + { + Browser.ThinkTime(thinkTime); + + return this.Execute(GetOptions("Next"), driver => + { + var nextIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.NextPage])); + + if (nextIcon.GetAttribute("disabled") != null) + return false; + else + nextIcon.Click(); + return true; + }); + } + + /// + /// Previouses the page. + /// + /// Used to simulate a wait time between human interactions. The Default is 2 seconds. + /// xrmBrowser.Grid.PreviousPage(); + public BrowserCommandResult PreviousPage(int thinkTime = Constants.DefaultThinkTime) + { + Browser.ThinkTime(thinkTime); + + return this.Execute(GetOptions("PreviousPage"), driver => + { + var previousIcon = driver.FindElement(By.XPath(Elements.Xpath[Reference.Grid.PreviousPage])); + + if (previousIcon.GetAttribute("disabled") != null) + return false; + else + previousIcon.Click(); + return true; + }); + } + + /// + /// Toggles the select all. + /// + /// Used to simulate a wait time between human interactions. The Default is 2 seconds. + /// xrmBrowser.Grid.SelectAllRecords(); + public BrowserCommandResult SelectAllRecords(int thinkTime = Constants.DefaultThinkTime) + { + Browser.ThinkTime(thinkTime); + + return this.Execute(GetOptions("ToggleSelectAll"), driver => + { + // We can check if any record selected by using + // driver.FindElements(By.ClassName("ms-crm-List-SelectedRow")).Count == 0 + // but this function doesn't check it. + var selectAll = driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Grid.ToggleSelectAll]), + "The Toggle SelectAll is not available."); + + selectAll.Click(); + + return true; + }); + } + + /// + /// Get the Grid Items + /// + /// Used to simulate a wait time between human interactions. The Default is 2 seconds. + public BrowserCommandResult> GetGridItems(int thinkTime = Constants.DefaultThinkTime) + { + Browser.ThinkTime(thinkTime); + + return this.Execute(GetOptions("Get Grid Items"), driver => + { + var returnList = new List(); + + var itemsTable = driver.FindElement(By.XPath(@"//*[@id=""gridBodyTable""]/tbody")); + var columnGroup = driver.FindElement(By.XPath(@"//*[@id=""gridBodyTable""]/colgroup")); + + var rows = itemsTable.FindElements(By.TagName("tr")); + + foreach (var row in rows) + { + if (!string.IsNullOrEmpty(row.GetAttribute("oid"))) + { + Guid id = Guid.Parse(row.GetAttribute("oid")); + var link = + $"{new Uri(driver.Url).Scheme}://{new Uri(driver.Url).Authority}/main.aspx?etn={row.GetAttribute("otypename")}&pagetype=entityrecord&id=%7B{id:D}%7D"; + + var item = new XrmGridItem + { + EntityName = row.GetAttribute("otypename"), + Id = id, + Url = new Uri(link) + }; + + var cells = row.FindElements(By.TagName("td")); + var idx = 0; + + foreach (var column in columnGroup.FindElements(By.TagName("col"))) + { + var name = column.GetAttribute("name"); + + if (!string.IsNullOrEmpty(name) + && column.GetAttribute("class").Contains(Elements.CssClass[Reference.Grid.DataColumn]) + && cells.Count > idx) + { + item[name] = cells[idx].Text; + } + + idx++; + } + + returnList.Add(item); + } + } + + return returnList; + }); + } + + /// + /// Set Lookup Value for the field + /// + /// The Field + /// The Index + /// xrmBrowser.Entity.SelectLookup("customerid", 0); + public BrowserCommandResult SelectLookup(string field, [Range(0, 9)]int index) + { + return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver => + { + if (driver.HasElement(By.Id(field))) + { + var input = driver.ClickWhenAvailable(By.Id(field)); + + if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) + throw new InvalidOperationException($"Field: {field} is not lookup"); + + input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])).Click(); + + var dialogName = $"Dialog_{field}_IMenu"; + var dialog = driver.FindElement(By.Id(dialogName)); + + var dialogItems = OpenDialog(dialog).Value; + + if (dialogItems.Count < index) + throw new InvalidOperationException($"List does not have {index + 1} items."); + + var dialogItem = dialogItems[index]; + dialogItem.Element.Click(); + } + else + throw new InvalidOperationException($"Field: {field} Does not exist"); + + return true; + }); + } + + /// + /// Set Lookup Value for the field + /// + /// The Field + /// The Lookup value + public BrowserCommandResult SelectLookup(string field, string value) + { + return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver => + { + if (driver.HasElement(By.Id(field))) + { + var input = driver.ClickWhenAvailable(By.Id(field)); + + if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) + throw new InvalidOperationException($"Field: {field} is not lookup"); + + var lookupIcon = input.FindElement(By.ClassName("Lookup_RenderButton_td")); + lookupIcon.Click(); + + var dialogName = $"Dialog_{field}_IMenu"; + var dialog = driver.FindElement(By.Id(dialogName)); + + var dialogItems = OpenDialog(dialog).Value; + + if (!dialogItems.Exists(x => x.Title == value)) + throw new InvalidOperationException($"List does not have {value}."); + + var dialogItem = dialogItems.Where(x => x.Title == value).First(); + dialogItem.Element.Click(); + } + else + throw new InvalidOperationException($"Field: {field} Does not exist"); + + return true; + }); + } + + /// + /// Set Lookup Value for the field + /// + /// The Field + /// The Open Lookup Page + public BrowserCommandResult SelectLookup(string field, bool openLookupPage = true) + { + return this.Execute(GetOptions($"Set Lookup Value: {field}"), driver => + { + if (driver.HasElement(By.Id(field))) + { + var input = driver.ClickWhenAvailable(By.Id(field)); + + if (input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])) == null) + throw new InvalidOperationException($"Field: {field} is not lookup"); + + input.FindElement(By.ClassName(Elements.CssClass[Reference.SetValue.LookupRenderClass])).Click(); + + var dialogName = $"Dialog_{field}_IMenu"; + var dialog = driver.FindElement(By.Id(dialogName)); + + var dialogItems = OpenDialog(dialog).Value; + + if (dialogItems.Any()) + { + var dialogItem = dialogItems.Last(); + dialogItem.Element.Click(); + } + } + else + throw new InvalidOperationException($"Field: {field} Does not exist"); + + return true; + }); + } + + /// + /// Opens the dialog + /// + /// + public BrowserCommandResult> OpenDialog(IWebElement dialog) + { + var list = new List(); + var dialogItems = dialog.FindElements(By.TagName("li")); + + foreach (var dialogItem in dialogItems) + { + if (dialogItem.GetAttribute("role") != null && dialogItem.GetAttribute("role") == "menuitem") + { + var links = dialogItem.FindElements(By.TagName("a")); + + if (links != null && links.Count > 1) + { + var title = links[1].GetAttribute("title"); + + list.Add(new XrmListItem() + { + Title = title, + Element = links[1] + }); + } + } + } + + return list; + } + + } } diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/BrowserDriverFactory.cs b/Microsoft.Dynamics365.UIAutomation.Browser/BrowserDriverFactory.cs index a67cb0ae..57e7fa21 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/BrowserDriverFactory.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/BrowserDriverFactory.cs @@ -35,10 +35,6 @@ public static IWebDriver CreateWebDriver(BrowserOptions options) ffService.HideCommandPromptWindow = options.HideDiagnosticWindow; driver = new FirefoxDriver(ffService); driver.Manage().Timeouts().ImplicitWait = new TimeSpan(0, 0, 5); - break; - case BrowserType.PhantomJs: - driver = new PhantomJSDriver(options.DriversPath); - break; case BrowserType.Edge: var edgeService = EdgeDriverService.CreateDefaultService(); diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/BrowserType.cs b/Microsoft.Dynamics365.UIAutomation.Browser/BrowserType.cs index 6e4539ec..d64f396d 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/BrowserType.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/BrowserType.cs @@ -9,7 +9,6 @@ public enum BrowserType Chrome = 2, Firefox = 4, Edge = 8, - PhantomJs = 16, - Remote = 32 + Remote = 16 } } \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs index 53904da1..5b70c2b6 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs @@ -32,6 +32,19 @@ public static IWebDriver ClickndWait(this IWebDriver driver, By by, TimeSpan tim return driver; } + public static void Click(this IWebElement element, bool ignoreStaleElementException = false) + { + try + { + element.Click(); + } + catch(StaleElementReferenceException ex) + { + if (!ignoreStaleElementException) + throw; + } + } + public static IWebElement ClickWhenAvailable(this IWebDriver driver, By by) { return ClickWhenAvailable(driver, by, Constants.DefaultTimeout); diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj b/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj index 5e53b2a0..92467764 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj @@ -25,7 +25,7 @@ pdbonly true - \bin\Release\ + bin\Release\ TRACE prompt 4