From ac5f6c28323b113fa40c9d0b9dcba893dfd2500a Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Sat, 8 Feb 2020 14:03:08 +0100 Subject: [PATCH 01/12] Fix Double Click on OpenRecord from Grid --- .../WebClient.cs | 35 +++++---- .../Extensions/SeleniumExtensions.cs | 72 +++++++++++++++---- .../UCI/GetValue.cs | 6 +- .../UCI/Read/OpenCase.cs | 8 +-- .../UCI/Read/OpenContact.cs | 26 +++++++ 5 files changed, 107 insertions(+), 40 deletions(-) diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs index d30382d1..0436cc11 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs @@ -1303,28 +1303,25 @@ internal BrowserCommandResult SwitchView(string viewName, int thinkTime = internal BrowserCommandResult OpenRecord(int index, int thinkTime = Constants.DefaultThinkTime, bool checkRecord = false) { ThinkTime(thinkTime); - return Execute(GetOptions("Open Grid Record"), driver => { - IWebElement control = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Grid.Container])); - - var xpathToFind = checkRecord - ? $"//div[@data-id='cell-{index}-1']" - : $"//div[contains(@data-id, 'cell-{index}')]//a"; - control.ClickWhenAvailable(By.XPath(xpathToFind), "An error occur trying to open the record at position {index}"); - - // Logic equivalent to fix #746 (by @rswafford) - //var xpathToFind = $"//div[@data-id='cell-{index}-1']"; - //control.WaitUntilClickable(By.XPath(xpathToFind), - // e => - // { - // e.Click(); - // if (!checkRecord) - // driver.DoubleClick(e); - // }, - // $"An error occur trying to open the record at position {index}" - // ); + var xpathToGrid = By.XPath(AppElements.Xpath[AppReference.Grid.Container]); + IWebElement control = driver.WaitUntilAvailable(xpathToGrid); + var xpathToCell = By.XPath( $".//div[@data-id='cell-{index}-1']"); + if (checkRecord) + control.ClickWhenAvailable(xpathToCell); + else + { + control.WaitUntilClickable(xpathToCell, + cell => + { + var emptyDiv = cell.FindElement(By.TagName("div")); + driver.DoubleClick(cell, cell.LeftTo(emptyDiv)); + }, + $"An error occur trying to open the record at position {index}" + ); + } driver.WaitForTransaction(); return true; }); diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs index 27ea225b..7b02b6d4 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs @@ -59,7 +59,7 @@ public static void Hover(this IWebElement element, IWebDriver driver, bool ignor try { Actions action = new Actions(driver); - action.MoveToElement(element).Build().Perform(); + action.MoveToElement(element).Perform(); } catch (StaleElementReferenceException) { @@ -71,13 +71,20 @@ public static void Hover(this IWebElement element, IWebDriver driver, bool ignor #endregion Click #region Double Click - - public static void DoubleClick(this IWebDriver driver, IWebElement element, bool ignoreStaleElementException = false) + + public static void DoubleClick(this IWebDriver driver, IWebElement element, Func offsetFunc = null, bool ignoreStaleElementException = true) { try - { - Actions actions = new Actions(driver); - actions.DoubleClick(element).Build().Perform(); + { + var actions = new Actions(driver); + if(offsetFunc == null) + actions = actions.DoubleClick(element); + else + { + var offset = offsetFunc(); + actions = actions.MoveToElement(element, offset.X, offset.Y).DoubleClick(); + } + actions.Perform(); } catch (StaleElementReferenceException) { @@ -85,13 +92,7 @@ public static void DoubleClick(this IWebDriver driver, IWebElement element, bool throw; } } - - public static void DoubleClick(this IWebDriver driver, By by, bool ignoreStaleElementException = false) - { - var element = driver.FindElement(by); - driver.DoubleClick(element, ignoreStaleElementException); - } - + #endregion #region Script Execution @@ -254,7 +255,7 @@ public static bool AlertIsPresent(this IWebDriver driver, TimeSpan timeout) public static IWebDriver LastWindow(this IWebDriver driver) => driver.SwitchTo().Window(driver.WindowHandles.Last()); - + /// /// Clears the focus from all elements. /// TODO: this implementation of the ClearFocus is clicking somewhere on the body, that may happen in some unwanted point, changing the results of the test. @@ -619,4 +620,45 @@ public static string ToTraceString(this FindElementEventArgs e) #endregion Args / Tracing } -} \ No newline at end of file +} + +public static class RelativePositions +{ + public static Func Above(this IWebElement outer, IWebElement inner) => + () => + { + int x = outer.Size.Width / 2; + int y = (inner.Location.Y - outer.Location.Y) / 2; + return new Point(x, y); + }; + + public static Func Below(this IWebElement outer, IWebElement inner) => + () => + { + int x = outer.Size.Width / 2; + var outerEnd = outer.Location + outer.Size; + var innerEnd = inner.Location + inner.Size; + int dBelow = outerEnd.Y - innerEnd.Y; + int y = innerEnd.Y + dBelow / 2; + return new Point(x, y); + }; + + public static Func LeftTo(this IWebElement outer, IWebElement inner) => + () => + { + int x = (inner.Location.X - outer.Location.X) / 2; + int y = outer.Size.Height / 2; + return new Point(x, y); + }; + + public static Func RightTo(this IWebElement outer, IWebElement inner) => + () => + { + var outerEnd = outer.Location + outer.Size; + var innerEnd = inner.Location + inner.Size; + int dRight = outerEnd.X - innerEnd.X; + int x = innerEnd.X + dRight / 2; ; + int y = outer.Size.Height / 2; + return new Point(x, y); + }; +} diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs index 0d5e2fc8..58028f29 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs @@ -88,14 +88,16 @@ public void UCITestGetValueFromDateTime() xrmApp.Entity.SetValue("name", "Test EasyRepro Opportunity"); - xrmApp.Entity.SetValue("estimatedclosedate", DateTime.Now, "M/d/yyyy h:mm tt"); + var dateTime = DateTime.Today.AddHours(10).AddMinutes(15); + xrmApp.Entity.SetValue("estimatedclosedate", dateTime, "M/d/yyyy", "h:mm tt"); xrmApp.ThinkTime(500); xrmApp.Entity.Save(); xrmApp.ThinkTime(2000); - var estimatedclosedate = xrmApp.Entity.GetValue("estimatedclosedate"); + var estimatedclosedate = xrmApp.Entity.GetValue(new DateTimeControl("estimatedclosedate")); + Assert.AreEqual(dateTime, estimatedclosedate); xrmApp.ThinkTime(500); } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenCase.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenCase.cs index a99a9af3..52a6355c 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenCase.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenCase.cs @@ -101,7 +101,7 @@ public void UCITestOpenCaseRetrieveHeaderValues_SetLookup() var client = new WebClient(TestSettings.Options); using (var xrmApp = new XrmApp(client)) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password); + xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); @@ -133,7 +133,7 @@ public void UCITestOpenCaseRetrieveHeaderValues_SetOptionSet() var client = new WebClient(TestSettings.Options); using (var xrmApp = new XrmApp(client)) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password); + xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); @@ -162,7 +162,7 @@ public void UCITestOpenCase_SetOptionSet() var client = new WebClient(TestSettings.Options); using (var xrmApp = new XrmApp(client)) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password); + xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); @@ -191,7 +191,7 @@ public void UCITestOpenCaseRetrieveHeaderValues_SetFields() var client = new WebClient(TestSettings.Options); using (var xrmApp = new XrmApp(client)) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password); + xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenContact.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenContact.cs index d4bdee09..8a52e796 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenContact.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenContact.cs @@ -187,5 +187,31 @@ public void UCITestOpenContactRelatedEntity() } } + + [TestMethod] + public void UCITestOpenContactRelatedEntity_SwitchView() + { + var client = new WebClient(TestSettings.Options); + using (var xrmApp = new XrmApp(client)) + { + xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); + + xrmApp.Navigation.OpenApp(UCIAppName.Sales); + + xrmApp.Navigation.OpenSubArea("Service", "Contacts"); + + xrmApp.ThinkTime(2000); + + xrmApp.Grid.SwitchView("Active Contacts"); + + xrmApp.ThinkTime(2000); + + xrmApp.Grid.SwitchView("My Active Contacts"); + + xrmApp.ThinkTime(2000); + + xrmApp.Grid.OpenRecord(1); + } + } } } \ No newline at end of file From 0bb508d514bcb52715f1c9dc106c4216d21520b9 Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Mon, 10 Feb 2020 08:04:46 +0100 Subject: [PATCH 02/12] Minor Fixes in Samples Tests ( from 109 tests, 93 passed) --- .../DTO/AppElementReference.cs | 12 +- .../Elements/BusinessProcessFlow.cs | 4 +- .../Elements/Entity.cs | 12 +- .../Elements/OnlineLogin.cs | 6 +- .../WebClient.cs | 620 ++++++++---------- .../XrmApp.cs | 5 + .../Extensions/SeleniumExtensions.cs | 36 +- .../Extensions/StringExtensions.cs | 14 +- .../Extensions/TimeExtensions.cs | 13 + .../InteractiveBrowser.cs | 1 - ...ft.Dynamics365.UIAutomation.Browser.csproj | 1 + .../TestSettings.cs | 27 +- .../BusinessProcessFlowNextStage.cs | 2 +- .../SwitchBusinessProcessFlow.cs | 4 +- .../UCI/CommandBar/AssignAccount.cs | 2 +- .../UCI/CommandBar/CloseOpportunity.cs | 13 +- .../UCI/CommandBar/CommandButton.cs | 3 +- .../UCI/CommandBar/DuplicateDetection.cs | 2 +- .../UCI/CommandBar/SelectDashboard.cs | 29 +- .../UCI/Create/CreateAccount.cs | 2 +- .../UCI/Create/CreateActivity.cs | 6 +- .../UCI/Create/CreateCase.cs | 13 +- .../UCI/Create/CreateLead.cs | 42 +- .../UCI/Create/CreateOpportunity.cs | 2 +- .../UCI/Delete/DeleteCase.cs | 4 +- .../UCI/Delete/DeleteOpportunity.cs | 6 +- .../UCI/GetValue.cs | 100 ++- .../GlobalSearch/GlobalCategorizedSearch.cs | 7 +- .../UCI/Login/Login.cs | 6 +- .../UCI/Navigation/OpenNavigation.cs | 104 +-- .../UCI/QuickCreate/QuickCreate.cs | 13 +- .../UCI/Read/HighlightAccount.cs | 2 +- .../UCI/Read/OpenAccount.cs | 2 +- .../UCI/Read/OpenCase.cs | 242 +++---- .../UCI/Read/OpenContact.cs | 14 +- .../UCI/Read/OpenLead.cs | 2 +- .../UCI/Read/OpenOpportunity.cs | 8 +- .../UCI/SetValue.cs | 8 +- .../UCI/TestsBase.cs | 38 +- .../UCI/Update/UpdateLead.cs | 2 +- .../BusinessProcessFlow.cs | 2 +- 41 files changed, 695 insertions(+), 736 deletions(-) create mode 100644 Microsoft.Dynamics365.UIAutomation.Browser/Extensions/TimeExtensions.cs diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/DTO/AppElementReference.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/DTO/AppElementReference.cs index 6626a0dd..278cd730 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/DTO/AppElementReference.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/DTO/AppElementReference.cs @@ -281,8 +281,8 @@ public static class AppElements { "Nav_SiteMapAreaMoreButton", "//button[@data-lp-id=\"sitemap-areaBar-more-btn\"]" }, { "Nav_SiteMapSingleArea", "//li[translate(@data-text,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz') = '[NAME]']" }, { "Nav_AppMenuContainer" , "//*[@id=\"taskpane-scroll-container\"]"}, - { "Nav_SettingsLauncherBar" , "//*[@data-id='[NAME]Launcher']"}, - { "Nav_SettingsLauncher" , "//*[@data-id='[NAME]']"}, + { "Nav_SettingsLauncherBar" , "//button[@data-id='[NAME]Launcher']"}, + { "Nav_SettingsLauncher" , "//div[@id='[NAME]Launcher']"}, { "Nav_GuidedHelp" , "//*[@id=\"helpLauncher\"]/button"}, //{ "Nav_AdminPortal" , "//*[@id=(\"id-5\")]"}, { "Nav_AdminPortal" , "//*[contains(@data-id,'officewaffle')]"}, @@ -299,8 +299,8 @@ public static class AppElements { "Nav_SitemapMenuItems", "//li[contains(@data-id,'sitemap-entity')]"}, { "Nav_SitemapSwitcherButton", "//button[contains(@data-id,'sitemap-areaSwitcher-expand-btn')]"}, { "Nav_SitemapSwitcherFlyout","//div[contains(@data-lp-id,'sitemap-area-switcher-flyout')]"}, - { "Nav_UCIAppContainer","//div[contains(@id,'AppLandingPageContentContainer')]"}, - { "Nav_UCIAppTile", ".//a[contains(@aria-label,'[NAME]')]"}, + { "Nav_UCIAppContainer","//div[@id='AppLandingPageContentContainer']"}, + { "Nav_UCIAppTile", "//div[@data-type='app-title' and @title='[NAME]']"}, //Grid @@ -329,7 +329,7 @@ public static class AppElements { "Entity_CloseOpportunityWin" , "//button[contains(@data-id,'MarkAsWon')]"}, { "Entity_CloseOpportunityLoss" , "//button[contains(@data-id,'MarkAsLost')]"}, { "Entity_Delete" , "//button[contains(@data-id,'Delete')]"}, - { "Entity_FormContainer" , "//*[@id=\"tab-section\"]"}, + { "Entity_FormContainer" , "//*[@data-id='editFormRoot']"}, { "Entity_Process" , "//button[contains(@data-id,'MBPF.ConvertTo')]"}, { "Entity_Save" , "//button[contains(@data-id, 'form-save-btn')]"}, { "Entity_SwitchProcess" , "//button[contains(@data-id,'SwitchProcess')]"}, @@ -392,7 +392,7 @@ public static class AppElements { "Entity_Header_FlyoutButton","//button[contains(@id,'headerFieldsExpandButton')]" }, { "Entity_Header_LookupFieldContainer", "//div[@data-id='header_[NAME].fieldControl-Lookup_[NAME]']" }, { "Entity_Header_TextFieldContainer", "//div[@data-id='header_[NAME].fieldControl-text-box-container']" }, - { "Entity_Header_OptionSetFieldContainer", "//div[@data-id='header_[NAME].fieldControl-option-set-container']" }, + { "Entity_Header_OptionSetFieldContainer", "//div[@data-id='header_[NAME]']" }, { "Entity_Header_DateTimeFieldContainer","//div[@data-id='header_[NAME]-FieldSectionItemContainer']" }, //CommandBar diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/BusinessProcessFlow.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/BusinessProcessFlow.cs index 00c1c4c2..839437e5 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/BusinessProcessFlow.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/BusinessProcessFlow.cs @@ -101,9 +101,9 @@ public void SetValue(BooleanItem optionSet) /// Sets the value of a LookupItem field /// /// LookupItem with the schema name of the field to retrieve - public void SetValue(LookupItem control, int index = 0) + public void SetValue(LookupItem control) { - _client.SetValue(control, index); + _client.SetValue(control); } /// diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/Entity.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/Entity.cs index d49f2afd..ee33bb61 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/Entity.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/Entity.cs @@ -434,9 +434,9 @@ public void SetValue(string field, string value) /// Sets the value of a Lookup. /// /// The lookup field name, value or index of the lookup. - public void SetValue(LookupItem control, int index = 0) + public void SetValue(LookupItem control) { - _client.SetValue(control, index); + _client.SetValue(control); } /// @@ -444,9 +444,9 @@ public void SetValue(LookupItem control, int index = 0) /// /// The activityparty lookup field name, value or index of the lookup. /// xrmApp.Entity.SetValue(new LookupItem[] { new LookupItem { Name = "to", Value = "A. Datum Corporation (sample)" } }); - public void SetValue(LookupItem[] controls, int index = 0) + public void SetValue(LookupItem[] controls) { - _client.SetValue(controls, index); + _client.SetValue(controls); } /// @@ -523,9 +523,9 @@ public void SelectForm(string formName) /// /// The activityparty lookup field name, value or index of the lookup. /// xrmApp.Entity.AddValues(new LookupItem[] { new LookupItem { Name = "to", Value = "A. Datum Corporation (sample)" } }); - public void AddValues(LookupItem[] controls, int index = 0) + public void AddValues(LookupItem[] controls) { - _client.AddValues(controls, index); + _client.AddValues(controls); } /// diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/OnlineLogin.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/OnlineLogin.cs index 2f00568c..770b6734 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/OnlineLogin.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/OnlineLogin.cs @@ -23,7 +23,7 @@ public void Login(Uri orgUrl) { _client.Login(orgUrl); - _client.InitializeModes(); + //_client.InitializeModes(); } /// @@ -37,7 +37,7 @@ public void Login(Uri orgUrl, SecureString username, SecureString password, Secu { _client.Login(orgUrl, username, password, mfaSecrectKey); - _client.InitializeModes(); + //_client.InitializeModes(); } /// @@ -52,7 +52,7 @@ public void Login(Uri orgUrl, SecureString username, SecureString password, Secu { _client.Login(orgUrl, username, password, mfaSecrectKey, redirectAction); - _client.InitializeModes(); + //_client.InitializeModes(); } } diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs index 0436cc11..b5fb4279 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs @@ -12,6 +12,7 @@ using System.Security; using System.Threading; using System.Web; +using OpenQA.Selenium.Interactions; using OtpNet; namespace Microsoft.Dynamics365.UIAutomation.Api.UCI @@ -49,7 +50,7 @@ internal BrowserCommandResult InitializeModes() if (Browser.Options.UCITestMode) queryParams += ",testmode=true"; if (Browser.Options.UCIPerformanceMode) queryParams += "&perf=true"; - if (!uri.Contains(queryParams) && !uri.Contains(System.Web.HttpUtility.UrlEncode(queryParams))) + if (!uri.Contains(queryParams) && !uri.Contains(HttpUtility.UrlEncode(queryParams))) { var testModeUri = uri + queryParams; @@ -247,8 +248,7 @@ private static IWebElement GetOtcInput(IWebDriver driver) private static void ClickStaySignedIn(IWebDriver driver) { var xpath = By.XPath(Elements.Xpath[Reference.Login.StaySignedIn]); - driver.WaitUntilVisible(xpath, new TimeSpan(0, 0, 5), - e => driver.ClickWhenAvailable(xpath)); + driver.ClickWhenAvailable(xpath, TimeSpan.FromSeconds(10)); } private static void SwitchToDefaultContent(IWebDriver driver) @@ -338,12 +338,14 @@ internal BrowserCommandResult OpenApp(string appName, int thinkTime = Cons return Execute(GetOptions($"Open App {appName}"), driver => { + driver.WaitForPageToLoad(); driver.SwitchTo().DefaultContent(); //Handle left hand Nav in Web Client - var success = TryOpenAppFromMenu(driver, appName, AppReference.Navigation.WebAppMenuButton) || - TryOpenAppFromMenu(driver, appName, AppReference.Navigation.UCIAppMenuButton) || - TryToClickInAppTile(appName, driver); + var success = TryToClickInAppTile(appName, driver) || + TryOpenAppFromMenu(driver, appName, AppReference.Navigation.WebAppMenuButton) || + TryOpenAppFromMenu(driver, appName, AppReference.Navigation.UCIAppMenuButton); + if (!success) throw new InvalidOperationException($"App Name {appName} not found."); @@ -356,36 +358,25 @@ internal BrowserCommandResult OpenApp(string appName, int thinkTime = Cons private bool TryOpenAppFromMenu(IWebDriver driver, string appName, string appMenuButton) { - try - { - var xpathToAppMenu = By.XPath(AppElements.Xpath[appMenuButton]); - bool found = driver.TryFindElement(xpathToAppMenu, out var appMenu); - if (found) - { - appMenu.Click(true); - OpenAppFromMenu(driver, appName); - } - - return found; - } - catch (Exception e) - { - throw new InvalidOperationException($"App Button {appMenuButton} not found.", e); - } + bool found = false; + var xpathToAppMenu = By.XPath(AppElements.Xpath[appMenuButton]); + driver.WaitUntilClickable(xpathToAppMenu, TimeSpan.FromSeconds(5), + appMenu => { + appMenu.Click(true); + OpenAppFromMenu(driver, appName); + found = true; + }); + return found; } internal void OpenAppFromMenu(IWebDriver driver, string appName) { var container = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Navigation.AppMenuContainer])); - - var buttons = container.FindElements(By.TagName("button")); - - var button = buttons.FirstOrDefault(x => x.Text.Trim() == appName); - - if (button != null) - button.Click(true); - else - throw new InvalidOperationException($"App Name {appName} not found."); + var xpathToButton = "//nav[@aria-hidden='false']//button//*[text()='[TEXT]']".Replace("[TEXT]", appName); + container.ClickWhenAvailable(By.XPath(xpathToButton), + TimeSpan.FromSeconds(1), + $"App Name {appName} not found." + ); driver.WaitUntilVisible(By.XPath(AppElements.Xpath[AppReference.Application.Shell])); driver.WaitUntilVisible(By.XPath(AppElements.Xpath[AppReference.Navigation.SiteMapLauncherButton])); @@ -393,15 +384,33 @@ internal void OpenAppFromMenu(IWebDriver driver, string appName) private static bool TryToClickInAppTile(string appName, IWebDriver driver) { - //Switch to frame 0 - driver.SwitchTo().Frame(0); - IWebElement tileContainer; - bool success = driver.TryFindElement(By.XPath(AppElements.Xpath[AppReference.Navigation.UCIAppContainer]), out tileContainer); - if (success) - { - var appTile = tileContainer.FindElement(By.XPath(AppElements.Xpath[AppReference.Navigation.UCIAppTile].Replace("[NAME]", appName))); - appTile.Click(true); - } + string message = null; + driver.WaitUntil( + d => + { + try + { + driver.SwitchTo().Frame("AppLandingPage"); + } + catch (NoSuchFrameException ex) + { + message = $"Frame AppLandingPage is not loaded. Exception: {ex.Message}"; + Trace.TraceWarning(message); + return false; + } + return true; + }, + TimeSpan.FromSeconds(30), + failureCallback: () => throw new InvalidOperationException(message) + ); + + var xpathToAppContainer = By.XPath(AppElements.Xpath[AppReference.Navigation.UCIAppContainer]); + var xpathToappTile = By.XPath(AppElements.Xpath[AppReference.Navigation.UCIAppTile].Replace("[NAME]", appName)); + + bool success = false; + driver.WaitUntilVisible(xpathToAppContainer, TimeSpan.FromSeconds(5), + appContainer => success = appContainer.ClickWhenAvailable(xpathToappTile, TimeSpan.FromSeconds(5)) != null + ); return success; } @@ -447,14 +456,14 @@ internal BrowserCommandResult OpenSubArea(string area, string subarea, int return Execute(GetOptions("Open Sub Area"), driver => { //If the subarea is already in the left hand nav, click it - var success = TryOpenSubArea(subarea); + var success = TryOpenSubArea(driver, subarea); if (!success) { success = TryOpenArea(area); if (!success) throw new InvalidOperationException($"Area with the name '{area}' not found. "); - success = TryOpenSubArea(subarea); + success = TryOpenSubArea(driver, subarea); if (!success) throw new InvalidOperationException($"No subarea with the name '{subarea}' exists inside of '{area}'."); } @@ -466,7 +475,6 @@ internal BrowserCommandResult OpenSubArea(string area, string subarea, int private static void WaitForLoadArea(IWebDriver driver) { - driver.WaitUntilVisible(By.XPath(AppElements.Xpath[AppReference.Grid.Container])); driver.WaitForPageToLoad(); driver.WaitForTransaction(); } @@ -475,21 +483,25 @@ public BrowserCommandResult OpenSubArea(string subarea) { return Execute(GetOptions("Open Unified Interface Sub-Area"), driver => { - var success = TryOpenSubArea(subarea); + var success = TryOpenSubArea(driver, subarea); WaitForLoadArea(driver); return success; }); } - private bool TryOpenSubArea(string subarea) + private bool TryOpenSubArea(IWebDriver driver, string subarea) { subarea = subarea.ToLowerString(); - var navSubAreas = OpenSubMenu(subarea).Value; + var navSubAreas = GetSubAreaMenuItems(driver); var found = navSubAreas.TryGetValue(subarea, out var element); if (found) - element.Click(true); - + { + var strSelected = element.GetAttribute("aria-selected"); + bool.TryParse(strSelected, out var selected); + if (!selected) + element.Click(true); + } return found; } @@ -506,18 +518,22 @@ public BrowserCommandResult OpenArea(string subarea) private bool TryOpenArea(string area) { area = area.ToLowerString(); - var areas = OpenAreas(area).Value; - - IWebElement menuItem = null; - bool foundMenuItem = areas.TryGetValue(area, out menuItem); - - if (foundMenuItem) - menuItem.Click(true); + var areas = OpenAreas(area); - return foundMenuItem; + IWebElement menuItem; + bool found = areas.TryGetValue(area, out menuItem); + if (found) + { + var strSelected = menuItem.GetAttribute("aria-checked"); + bool selected; + bool.TryParse(strSelected, out selected); + if (!selected) + menuItem.Click(true); + } + return found; } - public BrowserCommandResult> OpenAreas(string area, int thinkTime = Constants.DefaultThinkTime) + public Dictionary OpenAreas(string area, int thinkTime = Constants.DefaultThinkTime) { return Execute(GetOptions("Open Unified Interface Area"), driver => { @@ -533,7 +549,7 @@ public BrowserCommandResult> OpenAreas(string ar public Dictionary OpenMenu(int thinkTime = Constants.DefaultThinkTime) { - return this.Execute(GetOptions("Open Menu"), driver => + return Execute(GetOptions("Open Menu"), driver => { driver.ClickWhenAvailable(By.XPath(AppElements.Xpath[AppReference.Navigation.AreaButton])); @@ -544,7 +560,7 @@ public Dictionary OpenMenu(int thinkTime = Constants.Defaul public Dictionary OpenMenuFallback(string area, int thinkTime = Constants.DefaultThinkTime) { - return this.Execute(GetOptions("Open Menu"), driver => + return Execute(GetOptions("Open Menu"), driver => { //Make sure the sitemap-launcher is expanded - 9.1 var xpathSiteMapLauncherButton = By.XPath(AppElements.Xpath[AppReference.Navigation.SiteMapLauncherButton]); @@ -583,13 +599,7 @@ public Dictionary OpenMenuFallback(string area, int thinkTi else { var singleItem = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.Navigation.SiteMapSingleArea].Replace("[NAME]", area))); - char[] trimCharacters = - { - '', '\r', '\n', '', - '', '' - }; - - dictionary.Add(singleItem.Text.Trim(trimCharacters).ToLowerString(), singleItem); + dictionary.Add(singleItem.Text.ToLowerString(), singleItem); } return dictionary; @@ -612,13 +622,6 @@ private static void AddMenuItemsFrom(IWebDriver driver, string referenceToMenuIt ); } - private static Dictionary GetMenuItems(IWebElement menu) - { - var result = new Dictionary(); - AddMenuItems(menu, result); - return result; - } - private static void AddMenuItems(IWebElement menu, Dictionary dictionary) { var menuItems = menu.FindElements(By.TagName("li")); @@ -631,96 +634,86 @@ private static void AddMenuItems(IWebElement menu, Dictionary> OpenSubMenu(string subarea) + private static Dictionary GetSubAreaMenuItems(IWebDriver driver) { - return this.Execute(GetOptions($"Open Sub Menu: {subarea}"), driver => + var dictionary = new Dictionary(); + + //Sitemap without enableunifiedinterfaceshellrefresh + var hasPinnedSitemapEntity = driver.HasElement(By.XPath(AppElements.Xpath[AppReference.Navigation.PinnedSitemapEntity])); + if (!hasPinnedSitemapEntity) { - var dictionary = new Dictionary(); + // Close SiteMap launcher since it is open + var xpathToLauncherCloseButton = By.XPath(AppElements.Xpath[AppReference.Navigation.SiteMapLauncherCloseButton]); + driver.ClickWhenAvailable(xpathToLauncherCloseButton); - //Sitemap without enableunifiedinterfaceshellrefresh - var hasPinnedSitemapEntity = driver.HasElement(By.XPath(AppElements.Xpath[AppReference.Navigation.PinnedSitemapEntity])); - if (!hasPinnedSitemapEntity) - { - // Close SiteMap launcher since it is open - var xpathToLauncherCloseButton = By.XPath(AppElements.Xpath[AppReference.Navigation.SiteMapLauncherCloseButton]); - driver.ClickWhenAvailable(xpathToLauncherCloseButton); + driver.ClickWhenAvailable(By.XPath(AppElements.Xpath[AppReference.Navigation.SiteMapLauncherButton])); - driver.ClickWhenAvailable(By.XPath(AppElements.Xpath[AppReference.Navigation.SiteMapLauncherButton])); + var menuContainer = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Navigation.SubAreaContainer])); - var menuContainer = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Navigation.SubAreaContainer])); + var subItems = menuContainer.FindElements(By.TagName("li")); - var subItems = menuContainer.FindElements(By.TagName("li")); + foreach (var subItem in subItems) + { + // Check 'Id' attribute, NULL value == Group Header + var id = subItem.GetAttribute("id"); + if (string.IsNullOrEmpty(id)) + continue; + + // Filter out duplicate entity keys - click the first one in the list + var key = subItem.Text.ToLowerString(); + if (!dictionary.ContainsKey(key)) + dictionary.Add(key, subItem); + } - foreach (var subItem in subItems) - { - // Check 'Id' attribute, NULL value == Group Header - var id = subItem.GetAttribute("Id"); - if (string.IsNullOrEmpty(id)) - continue; - - // Filter out duplicate entity keys - click the first one in the list - var key = subItem.Text.ToLowerString(); - if (!dictionary.ContainsKey(key)) - dictionary.Add(key, subItem); - } + return dictionary; + } - return dictionary; - } + //Sitemap with enableunifiedinterfaceshellrefresh enabled + var menuShell = driver.FindElements(By.XPath(AppElements.Xpath[AppReference.Navigation.SubAreaContainer])); - //Sitemap with enableunifiedinterfaceshellrefresh enabled - var menuShell = driver.FindElements(By.XPath(AppElements.Xpath[AppReference.Navigation.SubAreaContainer])); + //The menu is broke into multiple sections. Gather all items. + foreach (IWebElement menuSection in menuShell) + { + var menuItems = menuSection.FindElements(By.XPath(AppElements.Xpath[AppReference.Navigation.SitemapMenuItems])); - //The menu is broke into multiple sections. Gather all items. - foreach (IWebElement menuSection in menuShell) + foreach (var menuItem in menuItems) { - var menuItems = menuSection.FindElements(By.XPath(AppElements.Xpath[AppReference.Navigation.SitemapMenuItems])); - - foreach (var menuItem in menuItems) - { - var text = menuItem.Text.ToLowerString(); - if (string.IsNullOrEmpty(text)) - continue; + var text = menuItem.Text.ToLowerString(); + if (string.IsNullOrEmpty(text)) + continue; - if (!dictionary.ContainsKey(text)) - dictionary.Add(text, menuItem); - } + if (!dictionary.ContainsKey(text)) + dictionary.Add(text, menuItem); } + } - return dictionary; - }); + return dictionary; } internal BrowserCommandResult OpenSettingsOption(string command, string dataId, int thinkTime = Constants.DefaultThinkTime) { - return this.Execute(GetOptions($"Open " + command + " " + dataId), driver => + return Execute(GetOptions($"Open " + command + " " + dataId), driver => { - var cmdButtonBar = AppElements.Xpath[AppReference.Navigation.SettingsLauncherBar].Replace("[NAME]", command); - var cmdLauncher = AppElements.Xpath[AppReference.Navigation.SettingsLauncher].Replace("[NAME]", command); + var xpathFlyout = By.XPath(AppElements.Xpath[AppReference.Navigation.SettingsLauncher].Replace("[NAME]", command)); + var xpathToFlyoutButton = By.XPath(AppElements.Xpath[AppReference.Navigation.SettingsLauncherBar].Replace("[NAME]", command)); - if (!driver.IsVisible(By.XPath(cmdLauncher))) + IWebElement flyout; + bool success = driver.TryFindElement(xpathFlyout, out flyout); + if (!success || !flyout.Displayed) { - driver.ClickWhenAvailable(By.XPath(cmdButtonBar)); - - Thread.Sleep(1000); - - driver.SetVisible(By.XPath(cmdLauncher), true); - driver.WaitUntilVisible(By.XPath(cmdLauncher)); + driver.ClickWhenAvailable(xpathToFlyoutButton, $"No command button exists that match with: {command}."); + flyout = driver.WaitUntilVisible(xpathFlyout, "Flyout menu did not became visible"); } - var menuContainer = driver.FindElement(By.XPath(cmdLauncher)); - var menuItems = menuContainer.FindElements(By.TagName("button")); + var menuItems = flyout.FindElements(By.TagName("button")); var button = menuItems.FirstOrDefault(x => x.GetAttribute("data-id").Contains(dataId)); - if (button != null) { button.Click(); - } - else - { - throw new InvalidOperationException($"No command with the exists inside of the Command Bar."); + return true; } - return true; + throw new InvalidOperationException($"No command with data-id: {dataId} exists inside of the command menu {command}"); }); } @@ -929,7 +922,7 @@ internal BrowserCommandResult AssignDialog(Dialogs.AssignTo to, string use var userOrTeamField = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.TextFieldLookup]), "User field unavailable"); var input = userOrTeamField.ClickWhenAvailable(By.TagName("input"), "User field unavailable"); input.SendKeys(userOrTeamName, true); - + ThinkTime(2000); //Pick the User from the list @@ -1037,48 +1030,20 @@ internal BrowserCommandResult HandleSaveDialog() }); } - /// - /// Opens the dialog - /// - /// - internal List GetListItems(IWebElement dialog) + private static ICollection GetListItems(IWebElement container, LookupItem control) { - var titlePath = By.XPath(".//label/span"); - var elementPath = By.XPath(".//div"); - - var result = GetListItems(dialog, titlePath, elementPath); + var name = control.Name; + var xpathToItems = By.XPath(AppElements.Xpath[AppReference.Entity.LookupFieldResultListItem].Replace("[NAME]", name)); + + //wait for complete the search + container.WaitUntil(d => d.FindVisible(By.XPath("//li/div/label/span"))?.Text?.Equals(control.Value, StringComparison.OrdinalIgnoreCase) == true); + + ICollection result = container.WaitUntil( + d => d.FindElements(xpathToItems), + failureCallback: () => throw new InvalidOperationException($"No Results Matching {control.Value} Were Found.") + ); return result; } - - private static List GetListItems(IWebElement dialog, By titlePath, By elementPath) - { - var list = new List(); - var dialogItems = dialog.FindElements(By.XPath(".//li")); - foreach (var dialogItem in dialogItems) - { - var titleLinks = dialogItem.FindElements(titlePath); - if (titleLinks == null || titleLinks.Count == 0) - continue; - - var divLinks = dialogItem.FindElements(elementPath); - if (divLinks == null || divLinks.Count == 0) - continue; - - var element = divLinks[0]; - var id = element.GetAttribute("id"); - var title = titleLinks[0].GetAttribute("innerText"); - - list.Add(new ListItem - { - Id = id, - Title = title, - Element = element - }); - } - - return list; - } - #endregion #region CommandBar @@ -1164,101 +1129,80 @@ internal BrowserCommandResult> GetCommandValues(bool includeMoreCom { ThinkTime(thinkTime); - return this.Execute(GetOptions("Get CommandBar Command Count"), driver => + return this.Execute(GetOptions("Get CommandBar Command Count"), driver => TryGetCommandValues(includeMoreCommandsValues, driver)); + } + + private static List TryGetCommandValues(bool includeMoreCommandsValues, IWebDriver driver) + { + const string moreCommandsLabel = "more commands"; + + //Find the button in the CommandBar + IWebElement ribbon = GetRibbon(driver); + + //Get the CommandBar buttons + Dictionary commandBarItems = GetMenuItems(ribbon); + bool hasMoreCommands = commandBarItems.TryGetValue(moreCommandsLabel, out var moreCommandsButton); + if (includeMoreCommandsValues && hasMoreCommands) { - IWebElement ribbon = null; - List commandValues = new List(); + moreCommandsButton.Click(true); - //Find the button in the CommandBar - if (driver.HasElement(By.XPath(AppElements.Xpath[AppReference.CommandBar.Container]))) - ribbon = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.CommandBar.Container])); + driver.WaitUntilVisible(By.XPath(AppElements.Xpath[AppReference.CommandBar.MoreCommandsMenu]), + menu => AddMenuItems(menu, commandBarItems), + "Unable to locate the 'More Commands' menu" + ); + } - if (ribbon == null) - { - if (driver.HasElement(By.XPath(AppElements.Xpath[AppReference.CommandBar.ContainerGrid]))) - ribbon = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.CommandBar.ContainerGrid])); - else - throw new InvalidOperationException("Unable to find the ribbon."); - } + var result = GetCommandNames(commandBarItems.Values); + return result; + } + + private static Dictionary GetMenuItems(IWebElement menu) + { + var result = new Dictionary(); + AddMenuItems(menu, result); + return result; + } - //Get the CommandBar buttons - var commandBarItems = ribbon.FindElements(By.TagName("li")); + private static List GetCommandNames(IEnumerable commandBarItems) + { + var result = new List(); + foreach (var value in commandBarItems) + { + string commandText = value.Text.Trim(); + if (string.IsNullOrWhiteSpace(commandText)) + continue; - foreach (var value in commandBarItems) + if (commandText.Contains("\r\n")) { - if (value.Text != "") - { - string commandText = value.Text.ToString(); - - if (commandText.Contains("\r\n")) - { - commandText = commandText.Substring(0, commandText.IndexOf("\r\n")); - } - - if (!commandValues.Contains(value.Text)) - { - commandValues.Add(commandText); - } - } + commandText = commandText.Substring(0, commandText.IndexOf("\r\n", StringComparison.Ordinal)); } + result.Add(commandText); + } + return result; + } - if (includeMoreCommandsValues) - { - if (commandBarItems.Any(x => x.GetAttribute("aria-label").Equals("More Commands", StringComparison.OrdinalIgnoreCase))) - { - //Click More Commands Button - commandBarItems.FirstOrDefault(x => x.GetAttribute("aria-label").Equals("More Commands", StringComparison.OrdinalIgnoreCase)).Click(true); - driver.WaitForTransaction(); - - //Click the button - var moreCommandsMenu = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.CommandBar.MoreCommandsMenu])); - - if (moreCommandsMenu != null) - { - var moreCommandsItems = moreCommandsMenu.FindElements(By.TagName("li")); - - foreach (var value in moreCommandsItems) - { - if (value.Text != "") - { - string commandText = value.Text.ToString(); - - if (commandText.Contains("\r\n")) - { - commandText = commandText.Substring(0, commandText.IndexOf("\r\n")); - } + private static IWebElement GetRibbon(IWebDriver driver) + { + var xpathCommandBarContainer = By.XPath(AppElements.Xpath[AppReference.CommandBar.Container]); + var xpathCommandBarGrid = By.XPath(AppElements.Xpath[AppReference.CommandBar.ContainerGrid]); - if (!commandValues.Contains(value.Text)) - { - commandValues.Add(commandText); - } - } - } - } - else - { - throw new InvalidOperationException("Unable to locate the 'More Commands' menu"); - } - } - else - { - throw new InvalidOperationException("No button matching 'More Commands' exists in the CommandBar"); - } - } + IWebElement ribbon = + driver.WaitUntilAvailable(xpathCommandBarContainer, 5.Seconds()) ?? + driver.WaitUntilAvailable(xpathCommandBarGrid, 5.Seconds()) ?? + throw new InvalidOperationException("Unable to find the ribbon."); - return commandValues; - }); + return ribbon; } #endregion #region Grid - public BrowserCommandResult> OpenViewPicker(int thinkTime = Constants.DefaultThinkTime) + public BrowserCommandResult> OpenViewPicker(int thinkTime = Constants.DefaultThinkTime) { ThinkTime(thinkTime); - return this.Execute(GetOptions("Open View Picker"), driver => + return Execute(GetOptions("Open View Picker"), driver => { driver.ClickWhenAvailable(By.XPath(AppElements.Xpath[AppReference.Grid.ViewSelector]), TimeSpan.FromSeconds(20), @@ -1267,16 +1211,22 @@ public BrowserCommandResult> OpenViewPicker(int think var viewContainer = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.Grid.ViewContainer])); var viewItems = viewContainer.FindElements(By.TagName("li")); - var dictionary = new Dictionary(); + var result = new Dictionary(); foreach (var viewItem in viewItems) { var role = viewItem.GetAttribute("role"); - if (role == "option") - dictionary.Add(viewItem.Text, viewItem.GetAttribute("id")); - } + if (role != "option") + continue; - return dictionary; + var key = viewItem.Text.ToLowerString(); + if(string.IsNullOrWhiteSpace(key)) + continue; + + if (!result.ContainsKey(key)) + result.Add(key, viewItem); + } + return result; }); } @@ -1284,18 +1234,16 @@ internal BrowserCommandResult SwitchView(string viewName, int thinkTime = { ThinkTime(thinkTime); - return this.Execute(GetOptions($"Switch View"), driver => + return Execute(GetOptions($"Switch View"), driver => { var views = OpenViewPicker().Value; Thread.Sleep(500); - if (!views.ContainsKey(viewName)) - { - throw new InvalidOperationException($"No view with the name '{viewName}' exists."); - } - - var viewId = views[viewName]; - driver.ClickWhenAvailable(By.Id(viewId)); + var key = viewName.ToLowerString(); + bool success = views.TryGetValue(key, out var view); + if (!success) + throw new InvalidOperationException($"No view with the name '{key}' exists."); + view.Click(true); return true; }); } @@ -1307,21 +1255,23 @@ internal BrowserCommandResult OpenRecord(int index, int thinkTime = Consta { var xpathToGrid = By.XPath(AppElements.Xpath[AppReference.Grid.Container]); IWebElement control = driver.WaitUntilAvailable(xpathToGrid); - - var xpathToCell = By.XPath( $".//div[@data-id='cell-{index}-1']"); + + Func action; if (checkRecord) - control.ClickWhenAvailable(xpathToCell); + action = e => e.Click(); else - { - control.WaitUntilClickable(xpathToCell, - cell => - { - var emptyDiv = cell.FindElement(By.TagName("div")); - driver.DoubleClick(cell, cell.LeftTo(emptyDiv)); - }, - $"An error occur trying to open the record at position {index}" - ); - } + action = e => e.DoubleClick(); + + var xpathToCell = By.XPath($".//div[@data-id='cell-{index}-1']"); + control.WaitUntilClickable(xpathToCell, + cell => + { + var emptyDiv = cell.FindElement(By.TagName("div")); + driver.Perform(action, cell, cell.LeftTo(emptyDiv)); + }, + $"An error occur trying to open the record at position {index}" + ); + driver.WaitForTransaction(); return true; }); @@ -1876,12 +1826,7 @@ internal BrowserCommandResult SetValue(string field, string value) if (!found) throw new NoSuchElementException($"Field with name {field} does not exist."); - input.Click(); - input.SendKeys(Keys.Control + "a"); - input.SendKeys(Keys.Backspace); - - if (!string.IsNullOrWhiteSpace(value)) - input.SendKeys(value, true); + SetInputValue(driver, input, value); // Needed to transfer focus out of special fields (email or phone) var label = fieldContainer.ClickIfVisible(By.TagName("label")); @@ -1892,40 +1837,50 @@ internal BrowserCommandResult SetValue(string field, string value) }); } + private void SetInputValue(IWebDriver driver, IWebElement input, string value) + { + input.SendKeys(Keys.Control + "a"); + input.SendKeys(Keys.Backspace); + driver.WaitForTransaction(); + + if (!string.IsNullOrWhiteSpace(value)) + { + input.SendKeys(value, true); + driver.WaitForTransaction(); + ThinkTime(3000); + } + } + /// /// Sets the value of a Lookup, Customer, Owner or ActivityParty Lookup which accepts only a single value. /// /// The lookup field name, value or index of the lookup. /// xrmApp.Entity.SetValue(new Lookup { Name = "prrimarycontactid", Value = "Rene Valdes (sample)" }); /// The default index position is 0, which will be the first result record in the lookup results window. Suppy a value > 0 to select a different record if multiple are present. - internal BrowserCommandResult SetValue(LookupItem control, int index = 0) + internal BrowserCommandResult SetValue(LookupItem control) { return Execute(GetOptions($"Set Lookup Value: {control.Name}"), driver => { - driver.WaitForTransaction(TimeSpan.FromSeconds(5)); + driver.WaitForTransaction(); var fieldContainer = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.TextFieldLookupFieldContainer].Replace("[NAME]", control.Name))); TryRemoveLookupValue(driver, fieldContainer, control); - TrySetValue(fieldContainer, control, index); + TrySetValue(driver, fieldContainer, control); return true; }); } - private void TrySetValue(IWebElement fieldContainer, LookupItem control, int index) + private void TrySetValue(IWebDriver driver, IWebElement fieldContainer, LookupItem control) { IWebElement input; bool found = fieldContainer.TryFindElement(By.TagName("input"), out input); string value = control.Value?.Trim(); if (found) - { - input.SendKeys(Keys.Control + "a"); - input.SendKeys(Keys.Backspace); - input.SendKeys(value, true); - } + SetInputValue(driver, input, value); - TrySetValue(fieldContainer, control, value, index); + TrySetValue(fieldContainer, control); } /// @@ -1934,25 +1889,26 @@ private void TrySetValue(IWebElement fieldContainer, LookupItem control, int ind /// The lookup field name, value or index of the lookup. /// xrmApp.Entity.SetValue(new Lookup[] { Name = "to", Value = "Rene Valdes (sample)" }, { Name = "to", Value = "Alpine Ski House (sample)" } ); /// The default index position is 0, which will be the first result record in the lookup results window. Suppy a value > 0 to select a different record if multiple are present. - internal BrowserCommandResult SetValue(LookupItem[] controls, int index = 0, bool clearFirst = true) + internal BrowserCommandResult SetValue(LookupItem[] controls, bool clearFirst = true) { var control = controls.First(); var controlName = control.Name; return Execute(GetOptions($"Set ActivityParty Lookup Value: {controlName}"), driver => { - driver.WaitForTransaction(TimeSpan.FromSeconds(5)); + driver.WaitForTransaction(); var fieldContainer = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.TextFieldLookupFieldContainer].Replace("[NAME]", controlName))); if (clearFirst) TryRemoveLookupValue(driver, fieldContainer, control); - TryToSetValue(fieldContainer, controls, index); + TryToSetValue(fieldContainer, controls); + return true; }); } - private void TryToSetValue(ISearchContext fieldContainer, LookupItem[] controls, int index) + private void TryToSetValue(ISearchContext fieldContainer, LookupItem[] controls) { IWebElement input; bool found = fieldContainer.TryFindElement(By.TagName("input"), out input); @@ -1972,51 +1928,50 @@ private void TryToSetValue(ISearchContext fieldContainer, LookupItem[] controls, } } - TrySetValue(fieldContainer, control, value, index); + TrySetValue(fieldContainer, control); } input.SendKeys(Keys.Escape); // IE wants to keep the flyout open on multi-value fields, this makes sure it closes } - private void TrySetValue(ISearchContext container, LookupItem control, string value, int index) + private void TrySetValue(ISearchContext container, LookupItem control) { + string value = control.Value; if (value == null) throw new InvalidOperationException($"No value has been provided for the LookupItem {control.Name}. Please provide a value or an empty string and try again."); if (value == string.Empty) - SetLookupByIndex(container, control, index); - - SetLookUpByValue(container, control, index); + SetLookupByIndex(container, control); + else + SetLookUpByValue(container, control); } - private void SetLookUpByValue(ISearchContext container, LookupItem control, int index) + private void SetLookUpByValue(ISearchContext container, LookupItem control) { var controlName = control.Name; var xpathToText = AppElements.Xpath[AppReference.Entity.LookupFieldNoRecordsText].Replace("[NAME]", controlName); var xpathToResultList = AppElements.Xpath[AppReference.Entity.LookupFieldResultList].Replace("[NAME]", controlName); - var byPath = By.XPath(xpathToText + "|" + xpathToResultList); - - container.WaitUntilAvailable(byPath, TimeSpan.FromSeconds(10)); + var bypathResultList = By.XPath(xpathToText + "|" + xpathToResultList); - var byPathToMenu = By.XPath(AppElements.Xpath[AppReference.Entity.TextFieldLookupMenu].Replace("[NAME]", controlName)); - var flyoutDialog = container.WaitUntilVisible(byPathToMenu); + container.WaitUntilAvailable(bypathResultList, TimeSpan.FromSeconds(10)); - var xpathResultListItem = By.XPath(AppElements.Xpath[AppReference.Entity.LookupFieldResultListItem].Replace("[NAME]", controlName)); - container.WaitUntilAvailable(xpathResultListItem, $"No Results Matching {control.Value} Were Found."); + var byPathToFlyout = By.XPath(AppElements.Xpath[AppReference.Entity.TextFieldLookupMenu].Replace("[NAME]", controlName)); + var flyoutDialog = container.WaitUntilClickable(byPathToFlyout); - List items = GetListItems(flyoutDialog); + var items = GetListItems(flyoutDialog, control); if (items.Count == 0) throw new InvalidOperationException($"List does not contain a record with the name: {control.Value}"); - + + int index = control.Index; if (index >= items.Count) throw new InvalidOperationException($"List does not contain {index + 1} records. Please provide an index value less than {items.Count} "); - var selectedItem = items[index]; - selectedItem.Element.Click(true); + var selectedItem = items.ElementAt(index); + selectedItem.Click(true); } - private void SetLookupByIndex(ISearchContext container, LookupItem control, int index) + private void SetLookupByIndex(ISearchContext container, LookupItem control) { var controlName = control.Name; var xpathToControl = By.XPath(AppElements.Xpath[AppReference.Entity.LookupResultsDropdown].Replace("[NAME]", controlName)); @@ -2025,15 +1980,16 @@ private void SetLookupByIndex(ISearchContext container, LookupItem control, int var xpathFieldResultListItem = By.XPath(AppElements.Xpath[AppReference.Entity.LookupFieldResultListItem].Replace("[NAME]", controlName)); container.WaitUntil(d => d.FindElements(xpathFieldResultListItem).Count > 0); - var items = GetListItems(lookupResultsDialog); + var items = GetListItems(lookupResultsDialog, control); if (items.Count == 0) throw new InvalidOperationException($"No results exist in the Recently Viewed flyout menu. Please provide a text value for {controlName}"); + int index = control.Index; if (index >= items.Count) throw new InvalidOperationException($"Recently Viewed list does not contain {index} records. Please provide an index value less than {items.Count}"); - var selectedItem = items[index]; - selectedItem.Element.Click(true); + var selectedItem = items.ElementAt(index); + selectedItem.Click(true); } /// @@ -2074,7 +2030,6 @@ private static void TrySetValue(IWebElement fieldContainer, OptionSet control) var options = listBox.FindElements(By.TagName("li")); SelectOption(options, value); - return; } @@ -2628,7 +2583,7 @@ internal BrowserCommandResult GetValue(MultiValueOptionSet expandCollapseButtons.First().Click(true); } - var returnValue = new MultiValueOptionSet {Name = option.Name}; + var returnValue = new MultiValueOptionSet { Name = option.Name }; xpath = AppElements.Xpath[AppReference.MultiSelect.SelectedRecordLabel].Replace("[NAME]", Elements.ElementId[option.Name]); var labelItems = driver.FindElements(By.XPath(xpath)); @@ -2934,7 +2889,7 @@ internal BrowserCommandResult SetHeaderValue(string field, string value) }); } - internal BrowserCommandResult SetHeaderValue(LookupItem control, int index = 0) + internal BrowserCommandResult SetHeaderValue(LookupItem control) { var controlName = control.Name; var xpathToContainer = AppElements.Xpath[AppReference.Entity.Header.LookupFieldContainer].Replace("[NAME]", controlName); @@ -2943,12 +2898,12 @@ internal BrowserCommandResult SetHeaderValue(LookupItem control, int index fieldContainer => { TryRemoveLookupValue(driver, fieldContainer, control); - TrySetValue(fieldContainer, control, index); + TrySetValue(driver, fieldContainer, control); return true; })); } - internal BrowserCommandResult SetHeaderValue(LookupItem[] controls, int index = 0, bool clearFirst = true) + internal BrowserCommandResult SetHeaderValue(LookupItem[] controls, bool clearFirst = true) { var control = controls.First(); var controlName = control.Name; @@ -2960,7 +2915,7 @@ internal BrowserCommandResult SetHeaderValue(LookupItem[] controls, int in if (clearFirst) TryRemoveLookupValue(driver, container, control); - TryToSetValue(container, controls, index); + TryToSetValue(container, controls); return true; })); } @@ -3178,11 +3133,11 @@ internal BrowserCommandResult SelectForm(string formName) }); } - internal BrowserCommandResult AddValues(LookupItem[] controls, int index = 0) + internal BrowserCommandResult AddValues(LookupItem[] controls) { - return this.Execute(GetOptions($"Add values {controls.First().Name}"), driver => + return Execute(GetOptions($"Add values {controls.First().Name}"), driver => { - SetValue(controls, index, false); + SetValue(controls, false); return true; }); @@ -3190,12 +3145,10 @@ internal BrowserCommandResult AddValues(LookupItem[] controls, int index = internal BrowserCommandResult RemoveValues(LookupItem[] controls) { - return this.Execute(GetOptions($"Remove values {controls.First().Name}"), driver => + return Execute(GetOptions($"Remove values {controls.First().Name}"), driver => { foreach (var control in controls) - { ClearValue(control, false); - } return true; }); @@ -4065,7 +4018,7 @@ public BrowserCommandResult OpenGlobalSearchRecord(string entity, int inde records[index].Click(true); - driver.WaitUntilClickable(By.XPath(AppElements.Xpath[AppReference.Entity.Form]), + driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Entity.Form]), TimeSpan.FromSeconds(30), "CRM Record is Unavailable or not finished loading. Timeout Exceeded" ); @@ -4142,7 +4095,7 @@ internal BrowserCommandResult SelectDashboard(string dashboardName, int th internal void EnablePerformanceCenter() { - Browser.Driver.Navigate().GoToUrl(string.Format("{0}&perf=true", Browser.Driver.Url)); + Browser.Driver.Navigate().GoToUrl($"{Browser.Driver.Url}&perf=true"); Browser.Driver.WaitForPageToLoad(); Browser.Driver.WaitForTransaction(); } @@ -4154,6 +4107,11 @@ internal void ThinkTime(int milliseconds) Browser.ThinkTime(milliseconds); } + internal void ThinkTime(TimeSpan timespan) + { + ThinkTime((int)timespan.TotalMilliseconds); + } + internal void Dispose() { Browser.Dispose(); diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/XrmApp.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/XrmApp.cs index 7dfd17fb..31192c13 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/XrmApp.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/XrmApp.cs @@ -43,6 +43,11 @@ public void ThinkTime(int milliseconds) { _client.ThinkTime(milliseconds); } + public void ThinkTime(TimeSpan timespan) + { + _client.ThinkTime((int)timespan.TotalMilliseconds); + } + public void Dispose() { _client?.Dispose(); diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs index 7b02b6d4..e7042cb8 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs @@ -7,6 +7,7 @@ using OpenQA.Selenium.Support.Events; using OpenQA.Selenium.Support.UI; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; @@ -70,21 +71,27 @@ public static void Hover(this IWebElement element, IWebDriver driver, bool ignor #endregion Click + public static void Click(this IWebDriver driver, IWebElement element, Func offsetFunc = null, bool ignoreStaleElementException = true) + => driver.Perform(a => a.Click(), element, offsetFunc, ignoreStaleElementException); + #region Double Click - + public static void DoubleClick(this IWebDriver driver, IWebElement element, Func offsetFunc = null, bool ignoreStaleElementException = true) + => driver.Perform(a => a.DoubleClick(), element, offsetFunc, ignoreStaleElementException); + + public static void Perform(this IWebDriver driver, Func action, IWebElement element, Func offsetFunc = null, bool ignoreStaleElementException = true) { try { var actions = new Actions(driver); if(offsetFunc == null) - actions = actions.DoubleClick(element); + actions = actions.MoveToElement(element); else { var offset = offsetFunc(); - actions = actions.MoveToElement(element, offset.X, offset.Y).DoubleClick(); + actions = actions.MoveToElement(element, offset.X, offset.Y); } - actions.Perform(); + action(actions).Perform(); } catch (StaleElementReferenceException) { @@ -92,6 +99,7 @@ public static void DoubleClick(this IWebDriver driver, IWebElement element, Func throw; } } + #endregion @@ -561,7 +569,27 @@ public static IWebElement WaitUntil(this ISearchContext driver, Func WaitUntil(this ISearchContext driver, Func> searchFunc, + TimeSpan? timeout = null, + Action> successCallback = null, Action failureCallback = null) + { + ICollection elements = null; + Predicate condition = d => + { + elements = searchFunc(d); + return elements.Count > 0; + }; + bool success = driver.WaitUntil(condition); + if (success) + successCallback?.Invoke(elements); + else + failureCallback?.Invoke(); + + return elements; + } + public static bool RepeatUntil(this IWebDriver driver, Action action, Predicate predicate, TimeSpan? timeout = null, int attemps = Constants.DefaultRetryAttempts, diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/StringExtensions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/StringExtensions.cs index e815bfc9..a793758d 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/StringExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/StringExtensions.cs @@ -39,8 +39,18 @@ public static string ToUnsecureString(this SecureString secureString) } public static string ToLowerString(this string value) { - char[] trimCharacters = { '', '\r', '\n', '', '', '', '' }; - return value.Trim() + char[] trimCharacters = { + '\r', + '\n', + (char) 60644, // + (char) 60932, // + (char) 59540, // + (char) 60038, // + (char) 61424, // + (char) 59902, // + }; + + return value?.Trim() .Trim(trimCharacters) .Replace("\r", string.Empty) .Replace("\n", string.Empty) diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/TimeExtensions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/TimeExtensions.cs new file mode 100644 index 00000000..78fdafd0 --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/TimeExtensions.cs @@ -0,0 +1,13 @@ +// Created by: Rodriguez Mustelier Angel (rodang) +// Modify On: 2020-02-09 14:05 + +using System; + +namespace Microsoft.Dynamics365.UIAutomation.Browser +{ + public static class TimeExtensions + { + public static TimeSpan Seconds(this int value) + => TimeSpan.FromSeconds(value); + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/InteractiveBrowser.cs b/Microsoft.Dynamics365.UIAutomation.Browser/InteractiveBrowser.cs index e70a7cb0..0c7b0c9d 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/InteractiveBrowser.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/InteractiveBrowser.cs @@ -148,7 +148,6 @@ public void ThinkTime(int milliseconds) } public void TakeWindowScreenShot(string path, ScreenshotImageFormat fileFormat) { - this.Driver.TakeScreenshot().SaveAsFile(path, fileFormat); } diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj b/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj index ee96443d..e8422a04 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj @@ -80,6 +80,7 @@ + diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs b/Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs index 4c35b0bd..f2a17190 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs @@ -21,7 +21,8 @@ public static class TestSettings private static readonly string DriversPath = ConfigurationManager.AppSettings["DriversPath"] ?? string.Empty; private static readonly bool UsePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? "true"); - public static BrowserOptions Options = new BrowserOptions + // Once you change this instance will affect all follow tests executions + public static BrowserOptions SharedOptions = new BrowserOptions { BrowserType = (BrowserType)Enum.Parse(typeof(BrowserType), Type), PrivateMode = UsePrivateMode, @@ -36,6 +37,22 @@ public static class TestSettings DriversPath = Path.IsPathRooted(DriversPath) ? DriversPath : Path.Combine(Directory.GetCurrentDirectory(), DriversPath) }; + // Create a new options instance, copy of the share, to use just in the current test, modifications in test will not affect other tests + public static BrowserOptions Options => new BrowserOptions + { + BrowserType = SharedOptions.BrowserType, + PrivateMode = SharedOptions.PrivateMode, + FireEvents = SharedOptions.FireEvents, + Headless = SharedOptions.Headless, + UserAgent = SharedOptions.UserAgent, + DefaultThinkTime = SharedOptions.DefaultThinkTime, + RemoteBrowserType = SharedOptions.RemoteBrowserType, + RemoteHubServer =SharedOptions.RemoteHubServer, + UCITestMode = SharedOptions.UCITestMode, + UCIPerformanceMode = SharedOptions.UCIPerformanceMode, + DriversPath = SharedOptions.DriversPath + }; + public static string GetRandomString(int minLen, int maxLen) { char[] Alphabet = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcefghijklmnopqrstuvwxyz0123456789").ToCharArray(); @@ -78,9 +95,9 @@ public static string GetRandomString(int minLen, int maxLen) public static class UCIAppName { - public static string Sales = "Sales Hub"; - public static string CustomerService = "Customer Service Hub"; - public static string Project = "Project Resource Hub"; - public static string FieldService = "Field Resource Hub"; + public const string Sales = "Sales Hub"; + public const string CustomerService = "Customer Service Hub"; + public const string Project = "Project Resource Hub"; + public const string FieldService = "Field Resource Hub"; } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/BusinessProcessFlow/BusinessProcessFlowNextStage.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/BusinessProcessFlow/BusinessProcessFlowNextStage.cs index a2402399..2b84670e 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/BusinessProcessFlow/BusinessProcessFlowNextStage.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/BusinessProcessFlow/BusinessProcessFlowNextStage.cs @@ -14,7 +14,7 @@ public class BusinessProcessFlowNextStage : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Leads"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Leads"); [TestMethod] public void UCITestBusinessProcessFlowNextStage() diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/BusinessProcessFlow/SwitchBusinessProcessFlow.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/BusinessProcessFlow/SwitchBusinessProcessFlow.cs index 8c6a48a6..39f07143 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/BusinessProcessFlow/SwitchBusinessProcessFlow.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/BusinessProcessFlow/SwitchBusinessProcessFlow.cs @@ -14,13 +14,11 @@ public class SwitchBusinessProcessFlow : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Leads"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Leads"); [TestMethod] public void UCITestSwitchBusinessProcessFlow() { - _xrmApp.Navigation.OpenSubArea("Sales", "Leads"); - _xrmApp.Grid.OpenRecord(0); //xrmApp.Entity.SwitchProcess("AccountEventingProcess"); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/AssignAccount.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/AssignAccount.cs index ccd325a2..b33adbd9 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/AssignAccount.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/AssignAccount.cs @@ -17,7 +17,7 @@ public class AssignAccount : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); [TestMethod] public void UCITestAssignAccount() diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CloseOpportunity.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CloseOpportunity.cs index f09a6373..b406038e 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CloseOpportunity.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CloseOpportunity.cs @@ -3,6 +3,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; +using Microsoft.Dynamics365.UIAutomation.Browser; namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI { @@ -15,13 +16,19 @@ public class CloseOpportunity : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Opportunities"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Opportunities"); + + public override void SetOptions(BrowserOptions options) + { + options.PrivateMode = false; + options.UCIPerformanceMode = true; + } [TestMethod] public void UCITestCloseOpportunity() { - _xrmApp.Navigation.OpenSubArea("Sales", "Opportunities"); - + _xrmApp.Grid.SwitchView("Open Opportunities"); + _xrmApp.Grid.OpenRecord(0); _xrmApp.CommandBar.ClickCommand("Close as Won"); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CommandButton.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CommandButton.cs index 3f43ca1b..586f8024 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CommandButton.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CommandButton.cs @@ -14,7 +14,7 @@ public class CommandButton : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); [TestMethod] public void UCITestNewCommandBarButton() @@ -33,6 +33,7 @@ public void UCITestRetrieveCommandBarValues() var includeMoreCommandValues = _xrmApp.CommandBar.GetCommandValues(true).Value; int totalCommandCount = includeMoreCommandValues.Count; + Assert.IsTrue(commandCount <= totalCommandCount); _xrmApp.ThinkTime(2000); } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/DuplicateDetection.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/DuplicateDetection.cs index 10db29e0..67ed2c38 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/DuplicateDetection.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/DuplicateDetection.cs @@ -15,7 +15,7 @@ public class DuplicateDetection : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Contacts"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Contacts"); [TestMethod] public void UCITestDuplicateDetection() { diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/SelectDashboard.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/SelectDashboard.cs index 123fb493..7689cda8 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/SelectDashboard.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/SelectDashboard.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using Microsoft.Dynamics365.UIAutomation.Api.UCI; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI @@ -8,18 +9,30 @@ namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI [TestClass] public class SelectDashboard : TestsBase { - [TestInitialize] - public override void InitTest() => base.InitTest(); - - [TestCleanup] - public override void FinishTest() => base.FinishTest(); - [TestMethod] public void UCITestSelectDashboard() { - _xrmApp.Navigation.ClickQuickLaunchButton("Dashboards"); + using (var xrmApp = CreateApp()) + { + NavigateTo(UCIAppName.Sales); - _xrmApp.Dashboard.SelectDashboard("My Knowledge Dashboard"); + xrmApp.Navigation.ClickQuickLaunchButton("Dashboards"); + + xrmApp.Dashboard.SelectDashboard("My Knowledge Dashboard"); + } + } + + [TestMethod] + public void UCITestSelectDashboard_SalesDashboard() + { + using (var xrmApp = CreateApp()) + { + NavigateTo(UCIAppName.Sales, "Sales", "Dashboards"); + + xrmApp.Dashboard.SelectDashboard("Sales Dashboard"); + + xrmApp.ThinkTime(5000); + } } } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs index 3634b825..8e10cb45 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs @@ -14,7 +14,7 @@ public class CreateAccount : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); [TestMethod] public void UCITestCreateAccount() diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateActivity.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateActivity.cs index 88153d40..bae2ae76 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateActivity.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateActivity.cs @@ -20,7 +20,7 @@ public void UCITestCreateActivity_SetDateTimes() xrmApp.Navigation.OpenApp(UCIAppName.Sales); - xrmApp.Navigation.OpenSubArea("My Work", "Activities"); + xrmApp.Navigation.OpenSubArea("Sales", "Activities"); xrmApp.CommandBar.ClickCommand("Appointment"); @@ -51,7 +51,7 @@ public void UCITestCreateActivity_ClearDateTimes() xrmApp.Navigation.OpenApp(UCIAppName.Sales); - xrmApp.Navigation.OpenSubArea("My Work", "Activities"); + xrmApp.Navigation.OpenSubArea("Sales", "Activities"); xrmApp.CommandBar.ClickCommand("Appointment"); @@ -96,7 +96,7 @@ public void UCITestCreateActivity_ClearHeaderDateTimes() xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); - xrmApp.Navigation.OpenSubArea("My Work", "Activities"); + xrmApp.Navigation.OpenSubArea("Sales", "Activities"); xrmApp.CommandBar.ClickCommand("Appointment"); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateCase.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateCase.cs index 35a5b6d3..dd5c371f 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateCase.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateCase.cs @@ -23,14 +23,21 @@ public void UCITestCreateCase() xrmApp.CommandBar.ClickCommand("New Case"); - xrmApp.ThinkTime(5000); + xrmApp.ThinkTime(2500); - xrmApp.Entity.SetValue("title", TestSettings.GetRandomString(5,10)); + xrmApp.Entity.SetValue("title", "Test Case "+ TestSettings.GetRandomString(5,10)); + + xrmApp.ThinkTime(2500); - LookupItem customer = new LookupItem { Name = "customerid", Value = "Test Lead" }; + LookupItem customer = new LookupItem { Name = "customerid", Value = "David", Index = 0}; xrmApp.Entity.SetValue(customer); + var customerName = xrmApp.Entity.GetValue(customer); + Assert.IsNotNull(customerName); + Assert.IsTrue(customerName.Contains("David")); + xrmApp.Entity.Save(); + xrmApp.ThinkTime(2500); } } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateLead.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateLead.cs index dda15451..bcff21f3 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateLead.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateLead.cs @@ -7,30 +7,44 @@ namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI { [TestClass] - public class CreateLeadUCI: TestsBase + public class CreateLeadUCI : TestsBase { + [TestInitialize] + public override void InitTest() => base.InitTest(); + + [TestCleanup] + public override void FinishTest() => base.FinishTest(); + + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Leads"); + [TestMethod] public void UCITestCreateLead() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); + _xrmApp.CommandBar.ClickCommand("New"); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); + _xrmApp.ThinkTime(5000); - xrmApp.Navigation.OpenSubArea("Sales", "Leads"); + _xrmApp.Entity.SetValue("subject", TestSettings.GetRandomString(5, 15)); + _xrmApp.Entity.SetValue("firstname", TestSettings.GetRandomString(5, 10)); + _xrmApp.Entity.SetValue("lastname", TestSettings.GetRandomString(5, 10)); + } + + [TestMethod] + public void UCITestCreateLead_SetHeaderStatus() + { + _xrmApp.CommandBar.ClickCommand("New"); - xrmApp.CommandBar.ClickCommand("New"); + _xrmApp.ThinkTime(5000); - xrmApp.ThinkTime(5000); + _xrmApp.Entity.SetValue("subject", TestSettings.GetRandomString(5, 15)); + _xrmApp.Entity.SetValue("firstname", TestSettings.GetRandomString(5, 10)); + _xrmApp.Entity.SetValue("lastname", TestSettings.GetRandomString(5, 10)); - xrmApp.Entity.SetValue("subject", TestSettings.GetRandomString(5,15)); - xrmApp.Entity.SetValue("firstname", TestSettings.GetRandomString(5,10)); - xrmApp.Entity.SetValue("lastname", TestSettings.GetRandomString(5,10)); + var status = new OptionSet { Name = "statuscode", Value = "Contacted" }; + _xrmApp.Entity.SetHeaderValue(status); - xrmApp.Entity.Save(); - } + string value = _xrmApp.Entity.GetHeaderValue(status); + Assert.AreEqual(status.Value, value); } } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateOpportunity.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateOpportunity.cs index 4431b5ec..5918ef01 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateOpportunity.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateOpportunity.cs @@ -64,7 +64,7 @@ public void UCITestCreateOpportunity_ClearHeaderDate() var client = new WebClient(TestSettings.Options); using (var xrmApp = new XrmApp(client)) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password); + xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); xrmApp.Navigation.OpenApp(UCIAppName.Sales); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Delete/DeleteCase.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Delete/DeleteCase.cs index 66e8608f..e946aa50 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Delete/DeleteCase.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Delete/DeleteCase.cs @@ -20,13 +20,15 @@ public void UCITestDeleteCase() xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); xrmApp.Navigation.OpenSubArea("Service", "Cases"); + + xrmApp.Grid.SwitchView("Active Cases"); xrmApp.Grid.OpenRecord(0); //Click the Delete button from the command bar xrmApp.CommandBar.ClickCommand("Delete", "", false); //Set to true if command is a part of the More Commands menu - xrmApp.Dialogs.ConfirmationDialog(true); //Click OK on the Delete confirmation dialog (false to cancel) + xrmApp.Dialogs.ConfirmationDialog(false); //Click OK on the Delete confirmation dialog (false to cancel) xrmApp.ThinkTime(3000); } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Delete/DeleteOpportunity.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Delete/DeleteOpportunity.cs index 9e860db4..01f60b9e 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Delete/DeleteOpportunity.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Delete/DeleteOpportunity.cs @@ -20,18 +20,18 @@ public void UCITestDeleteOpportunity() xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenSubArea("Sales", "Opportunities"); + + xrmApp.Grid.SwitchView("Open Opportunities"); xrmApp.Grid.OpenRecord(0); //Click the Delete button from the command bar xrmApp.CommandBar.ClickCommand("Delete", "", false); //Set to true if command is a part of the More Commands menu - xrmApp.Dialogs.ConfirmationDialog(true); //Click OK on the Delete confirmation dialog (false to cancel) + xrmApp.Dialogs.ConfirmationDialog(false); //Click OK on the Delete confirmation dialog (false to cancel) xrmApp.ThinkTime(3000); - } - } } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs index 58028f29..e0bc71ef 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs @@ -8,98 +8,72 @@ namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI { [TestClass] - public class GetValueUci: TestsBase + public class GetValueUci : TestsBase { - [TestMethod] - public void UCITestGetValueFromOptionSet() - { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); + [TestInitialize] + public override void InitTest() => base.InitTest(); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); + [TestCleanup] + public override void FinishTest() => base.FinishTest(); - xrmApp.Navigation.OpenSubArea("Sales", "Contacts"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales); - xrmApp.Grid.OpenRecord(0); + [TestMethod] + public void UCITestGetValueFromOptionSet() + { + _xrmApp.Navigation.OpenSubArea("Contacts"); - var options = xrmApp.Entity.GetValue(new OptionSet { Name = "preferredcontactmethodcode" }); - } + _xrmApp.Grid.OpenRecord(0); + string option = _xrmApp.Entity.GetValue(new OptionSet {Name = "preferredcontactmethodcode"}); + Assert.IsNotNull(option); } [TestMethod] public void UCITestGetValueFromLookup() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - - xrmApp.Navigation.OpenApp(UCIAppName.Sales); + _xrmApp.Navigation.OpenSubArea("Accounts"); - xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); + _xrmApp.Grid.OpenRecord(0); - xrmApp.Grid.OpenRecord(0); - - xrmApp.ThinkTime(2000); - string lookupValue = xrmApp.Entity.GetValue(new LookupItem { Name = "primarycontactid" }); - } + _xrmApp.ThinkTime(2000); + string lookupValue = _xrmApp.Entity.GetValue(new LookupItem {Name = "primarycontactid"}); + Assert.IsNotNull(lookupValue); } [TestMethod] public void UCITestActivityPartyGetValue() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - - xrmApp.Navigation.OpenApp(UCIAppName.Sales); + _xrmApp.Navigation.OpenSubArea("Activities"); - xrmApp.Navigation.OpenSubArea("Sales", "Activities"); + _xrmApp.Grid.SwitchView("All Phone Calls"); + _xrmApp.ThinkTime(500); - xrmApp.Grid.SwitchView("All Phone Calls"); - xrmApp.ThinkTime(500); + _xrmApp.Grid.OpenRecord(0); + _xrmApp.ThinkTime(500); - xrmApp.Grid.OpenRecord(0); - xrmApp.ThinkTime(500); - - var to = xrmApp.Entity.GetValue(new LookupItem[] { new LookupItem { Name = "to" } }); - xrmApp.ThinkTime(500); - } + var to = _xrmApp.Entity.GetValue(new [] {new LookupItem {Name = "to"}}); + Assert.IsNotNull(to); + _xrmApp.ThinkTime(500); } [TestMethod] public void UCITestGetValueFromDateTime() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - - xrmApp.Navigation.OpenApp(UCIAppName.Sales); - - xrmApp.Navigation.OpenSubArea("Sales", "Opportunities"); - xrmApp.ThinkTime(500); - - xrmApp.CommandBar.ClickCommand("New"); - - xrmApp.Entity.SetValue("name", "Test EasyRepro Opportunity"); + _xrmApp.Navigation.OpenSubArea("Opportunities"); + _xrmApp.ThinkTime(500); - var dateTime = DateTime.Today.AddHours(10).AddMinutes(15); - xrmApp.Entity.SetValue("estimatedclosedate", dateTime, "M/d/yyyy", "h:mm tt"); - xrmApp.ThinkTime(500); + _xrmApp.CommandBar.ClickCommand("New"); - xrmApp.Entity.Save(); + _xrmApp.Entity.SetValue("name", "Test EasyRepro Opportunity"); - xrmApp.ThinkTime(2000); + var dateTime = DateTime.Today.AddHours(10).AddMinutes(15); + _xrmApp.Entity.SetHeaderValue("estimatedclosedate", dateTime); + _xrmApp.ThinkTime(500); - var estimatedclosedate = xrmApp.Entity.GetValue(new DateTimeControl("estimatedclosedate")); - Assert.AreEqual(dateTime, estimatedclosedate); - xrmApp.ThinkTime(500); - } + var estimatedclosedate = _xrmApp.Entity.GetHeaderValue(new DateTimeControl("estimatedclosedate")); + Assert.AreEqual(dateTime, estimatedclosedate); + _xrmApp.ThinkTime(500); } } -} +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GlobalSearch/GlobalCategorizedSearch.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GlobalSearch/GlobalCategorizedSearch.cs index 0b97c0ac..bbf9b53a 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GlobalSearch/GlobalCategorizedSearch.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GlobalSearch/GlobalCategorizedSearch.cs @@ -12,11 +12,8 @@ public class GlobalCategorizedSearchUci : TestsBase [TestMethod] public void UCITestGlobalCategorizedSearch() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) + using (var xrmApp = CreateApp()) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenGlobalSearch(); @@ -27,7 +24,7 @@ public void UCITestGlobalCategorizedSearch() xrmApp.GlobalSearch.FilterWith("Account"); - xrmApp.GlobalSearch.OpenRecord("account", 0); + xrmApp.GlobalSearch.OpenRecord("account", 0); } } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Login/Login.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Login/Login.cs index 212e42b3..6901b3af 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Login/Login.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Login/Login.cs @@ -1,6 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI.Login +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI { [TestClass] public class Login : TestsBase @@ -11,7 +11,7 @@ public class Login : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); [TestMethod] public void MultiFactorLogin() @@ -20,7 +20,7 @@ public void MultiFactorLogin() _xrmApp.CommandBar.ClickCommand("New"); - _xrmApp.Entity.SetValue("name", _timed("Test API Account")); + _xrmApp.Entity.SetValue("name", "Test API Account" + TestSettings.GetRandomString(5,5) ); _xrmApp.Entity.SetValue("telephone1", "555-555-5555"); } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Navigation/OpenNavigation.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Navigation/OpenNavigation.cs index 495aeb40..ad7c7aec 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Navigation/OpenNavigation.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Navigation/OpenNavigation.cs @@ -1,97 +1,43 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Dynamics365.UIAutomation.Api.UCI; +using Microsoft.Dynamics365.UIAutomation.Browser; namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI { [TestClass] public class OpenNavigationUci: TestsBase { - [TestMethod] - public void UCITestOpenOptions() - { - TestSettings.Options.UCIPerformanceMode = false; - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); - xrmApp.Navigation.OpenOptions(); - xrmApp.Navigation.OpenOptInForLearningPath(); - xrmApp.Navigation.OpenPrivacy(); - xrmApp.Navigation.SignOut(); - } - } - - [TestMethod] - public void UCITestOpenGuidedHelp() - { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); - xrmApp.Navigation.OpenGuidedHelp(); - } - } - - [TestMethod] - public void UCITestOpenSoftwareLicensing() + public void ExecuteNavegationCommand(Action command) { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) + var options = TestSettings.Options; + options.UCIPerformanceMode = false; + + using (var xrmApp = CreateApp(options)) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); xrmApp.Navigation.OpenApp(UCIAppName.Sales); - xrmApp.Navigation.OpenSoftwareLicensing(); + command(xrmApp.Navigation); + xrmApp.ThinkTime(3.Seconds()); } } - - [TestMethod] - public void UCITestOpenToastNotifications() - { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); - xrmApp.Navigation.OpenToastNotifications(); - } - } - - [TestMethod] - public void UCITestOpenAbout() - { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); - xrmApp.Navigation.OpenAbout(); - } - } - - [TestMethod] - public void UCITestOpenAppSettings() - { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); - xrmApp.Navigation.OpenSubArea("App Settings", "PDF generation"); - } - } - + [TestMethod] public void UCITestOpenOptions() => ExecuteNavegationCommand(n => n.OpenOptions()); + [TestMethod] public void UCITestOpenPrivacy() => ExecuteNavegationCommand(n => n.OpenPrivacy()); + [TestMethod] public void UCITestSignOut() => ExecuteNavegationCommand(n => n.SignOut()); + [TestMethod] public void UCITestOptInForLearningPath() => ExecuteNavegationCommand(n => n.OpenOptInForLearningPath()); + [TestMethod] public void UCITestOpenGuidedHelp () => ExecuteNavegationCommand(n => n.OpenGuidedHelp()); + [TestMethod] public void UCITestOpenSoftwareLicensing () => ExecuteNavegationCommand(n => n.OpenSoftwareLicensing()); + [TestMethod] public void UCITestOpenToastNotifications () => ExecuteNavegationCommand(n => n.OpenToastNotifications()); + [TestMethod] public void UCITestOpenAbout () => ExecuteNavegationCommand(n => n.OpenAbout()); + [TestMethod] public void UCITest_AppSettings_PDFgeneration () => ExecuteNavegationCommand(n => n.OpenSubArea("App Settings", "PDF generation")); + [TestMethod] public void UCITestOpenHelp() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) + using (var xrmApp = CreateApp()) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password); xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenArea("Help and Support"); xrmApp.Navigation.OpenSubArea("Help Center"); @@ -101,11 +47,8 @@ public void UCITestOpenHelp() [TestMethod] public void UCITestOpenRelatedCommonActivities() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) + using (var xrmApp = CreateApp()) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); @@ -119,11 +62,8 @@ public void UCITestOpenRelatedCommonActivities() [TestMethod] public void UCITestOpenGroupSubArea() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) + using (var xrmApp = CreateApp()) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenGroupSubArea("Customers", "Accounts"); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickCreate/QuickCreate.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickCreate/QuickCreate.cs index ea930aee..5dd774b0 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickCreate/QuickCreate.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickCreate/QuickCreate.cs @@ -3,6 +3,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Dynamics365.UIAutomation.Api.UCI; using System; +using Microsoft.Dynamics365.UIAutomation.Browser; namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI { @@ -35,23 +36,21 @@ public void UCITestQuickCreateContact() [TestMethod] public void UCITestQuickCreateCase() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) + using (var xrmApp = CreateApp()) { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); xrmApp.Navigation.QuickCreate("case"); - xrmApp.QuickCreate.SetValue(new LookupItem { Name = "customerid", Value = "" }); + xrmApp.QuickCreate.SetValue(new LookupItem { Name = "customerid", Value = "Adventure" }); xrmApp.QuickCreate.SetValue("title", TestSettings.GetRandomString(5, 10)); - xrmApp.QuickCreate.SetValue(new OptionSet() { Name = "casetypecode", Value = "Problem" }); + xrmApp.QuickCreate.SetValue(new OptionSet { Name = "casetypecode", Value = "Problem" }); xrmApp.QuickCreate.Save(); - + + xrmApp.ThinkTime(5.Seconds()); } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/HighlightAccount.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/HighlightAccount.cs index f0f819d7..70714fff 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/HighlightAccount.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/HighlightAccount.cs @@ -24,10 +24,10 @@ public void UCITestHighlightActiveAccount() xrmApp.Grid.Search("Adventure"); xrmApp.Grid.HighLightRecord(0); //Ticks the box, allowing you to Edit / Delete (Command) if you so desire + xrmApp.CommandBar.ClickCommand("Edit"); xrmApp.ThinkTime(3000); - } } } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenAccount.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenAccount.cs index 00e89ca9..1729ddc1 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenAccount.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenAccount.cs @@ -16,7 +16,7 @@ public class OpenAccountUCI : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); [TestMethod] public void UCITestOpenActiveAccount() diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenCase.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenCase.cs index 52a6355c..c26fce9b 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenCase.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenCase.cs @@ -9,221 +9,169 @@ namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI { [TestClass] - public class OpenCaseUCI: TestsBase + public class OpenCaseUCI : TestsBase { - [TestMethod] - public void UCITestOpenActiveCase() - { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); + [TestInitialize] + public override void InitTest() => base.InitTest(); - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); + [TestCleanup] + public override void FinishTest() => base.FinishTest(); - xrmApp.Navigation.OpenSubArea("Service", "Cases"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.CustomerService, "Service", "Cases"); - xrmApp.Grid.Search("Contacted"); + [TestMethod] + public void UCITestOpenActiveCase() + { + _xrmApp.Grid.Search("*Service"); - xrmApp.Grid.OpenRecord(0); - } + _xrmApp.Grid.OpenRecord(0); } [TestMethod] public void UCITestOpenCase() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); + _xrmApp.Grid.SwitchView("Active Cases"); + _xrmApp.Grid.OpenRecord(0); - xrmApp.Navigation.OpenSubArea("Service", "Cases"); + Field ticketNumber = _xrmApp.Entity.GetField("ticketnumber"); + Assert.IsNotNull(ticketNumber); + + Field subject = _xrmApp.Entity.GetField("subjectid"); + Assert.IsNotNull(subject); - xrmApp.Grid.OpenRecord(0); + Field description = _xrmApp.Entity.GetField("description"); + Assert.IsNotNull(description); - Field ticketNumber = xrmApp.Entity.GetField("ticketnumber"); - Field subject = xrmApp.Entity.GetField("subjectid"); - Field description = xrmApp.Entity.GetField("description"); - Field mobilePhone = xrmApp.Entity.GetField("mobilephone"); - } + Field mobilePhone = _xrmApp.Entity.GetField("mobilephone"); + Assert.IsNotNull(mobilePhone); } [TestMethod] public void UCITestOpenCaseById() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); + _xrmApp.Grid.SwitchView("Active Cases"); + _xrmApp.Grid.OpenRecord(0); + string number = _xrmApp.Entity.GetValue("ticketnumber"); + Assert.IsNotNull(number); - xrmApp.Navigation.OpenSubArea("Service", "Cases"); + // For proper test usage, please update the recordId below to a valid Case recordId + Guid recordId = _xrmApp.Entity.GetObjectId(); - // For proper test usage, please update the recordId below to a valid Case recordId - Guid recordId = new Guid("ba9a3931-675d-e911-a852-000d3a372393"); + _xrmApp.Navigation.OpenSubArea("Service", "Cases"); - xrmApp.Entity.OpenEntity("incident", recordId); - } + string firstCaseNumber = number; + _xrmApp.Entity.OpenEntity("incident", recordId); + number = _xrmApp.Entity.GetValue("ticketnumber"); + Assert.IsNotNull(number); + Assert.AreEqual(firstCaseNumber, number); } [TestMethod] public void UCITestOpenCaseRetrieveHeaderValues() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); - - xrmApp.Navigation.OpenSubArea("Service", "Cases"); + _xrmApp.Grid.SwitchView("Active Cases"); - xrmApp.Grid.SwitchView("Active Cases"); + _xrmApp.Grid.OpenRecord(0); - xrmApp.Grid.OpenRecord(0); + LookupItem ownerId = new LookupItem {Name = "ownerid"}; + string ownerIdValue = _xrmApp.Entity.GetHeaderValue(ownerId); - LookupItem ownerId = new LookupItem { Name = "ownerid" }; - string ownerIdValue = xrmApp.Entity.GetHeaderValue(ownerId); + OptionSet priorityCode = new OptionSet {Name = "prioritycode"}; + string priorityCodeValue = _xrmApp.Entity.GetHeaderValue(priorityCode); - OptionSet priorityCode = new OptionSet { Name = "prioritycode" }; - string priorityCodeValue = xrmApp.Entity.GetHeaderValue(priorityCode); - - xrmApp.ThinkTime(2000); - } + _xrmApp.ThinkTime(2000); } [TestMethod] public void UCITestOpenCaseRetrieveHeaderValues_SetLookup() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); + _xrmApp.Grid.SwitchView("Active Cases"); - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); + _xrmApp.Grid.OpenRecord(0); - xrmApp.Navigation.OpenSubArea("Service", "Cases"); + LookupItem ownerId = new LookupItem {Name = "ownerid"}; + string ownerIdValue = _xrmApp.Entity.GetHeaderValue(ownerId); - xrmApp.Grid.SwitchView("Active Cases"); + _client.Browser.Driver.ClearFocus(); - xrmApp.Grid.OpenRecord(0); + ownerId.Value = "Angel Rodriguez"; + _xrmApp.Entity.SetHeaderValue(ownerId); - LookupItem ownerId = new LookupItem { Name = "ownerid" }; - string ownerIdValue = xrmApp.Entity.GetHeaderValue(ownerId); + ownerIdValue = _xrmApp.Entity.GetHeaderValue(ownerId); + Assert.AreEqual(ownerId.Value, ownerIdValue); - client.Browser.Driver.ClearFocus(); - - ownerId.Value = "Angel Rodriguez"; - xrmApp.Entity.SetHeaderValue(ownerId); - - ownerIdValue = xrmApp.Entity.GetHeaderValue(ownerId); - Assert.AreEqual(ownerId.Value, ownerIdValue); - - xrmApp.ThinkTime(2000); - } + _xrmApp.ThinkTime(2000); } [TestMethod] public void UCITestOpenCaseRetrieveHeaderValues_SetOptionSet() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); + _xrmApp.Grid.SwitchView("Active Cases"); - xrmApp.Navigation.OpenSubArea("Service", "Cases"); + _xrmApp.Grid.OpenRecord(0); - xrmApp.Grid.SwitchView("Active Cases"); - - xrmApp.Grid.OpenRecord(0); - - OptionSet priorityCode = new OptionSet { Name = "prioritycode" }; - string priorityCodeValue = xrmApp.Entity.GetHeaderValue(priorityCode); - - priorityCode.Value = "High"; - xrmApp.Entity.SetHeaderValue(priorityCode); + OptionSet priorityCode = new OptionSet + { + Name = "prioritycode", + Value = "High" + }; + + _xrmApp.Entity.SetHeaderValue(priorityCode); - priorityCodeValue = xrmApp.Entity.GetHeaderValue(priorityCode); - Assert.AreEqual(priorityCode.Value, priorityCodeValue); + string priorityCodeValue = _xrmApp.Entity.GetHeaderValue(priorityCode); + Assert.AreEqual(priorityCode.Value, priorityCodeValue); - xrmApp.ThinkTime(2000); - } + _xrmApp.ThinkTime(2000); } [TestMethod] public void UCITestOpenCase_SetOptionSet() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); + _xrmApp.Grid.SwitchView("Active Cases"); - xrmApp.Navigation.OpenSubArea("Service", "Cases"); + _xrmApp.Grid.OpenRecord(0); - xrmApp.Grid.SwitchView("Active Cases"); - - xrmApp.Grid.OpenRecord(0); - - OptionSet priorityCode = new OptionSet { Name = "prioritycode" }; - string priorityCodeValue = xrmApp.Entity.GetValue(priorityCode); - - priorityCode.Value = "High"; - xrmApp.Entity.SetValue(priorityCode); + OptionSet priorityCode = new OptionSet + { + Name = "prioritycode", + Value = "High" + }; + _xrmApp.Entity.SetValue(priorityCode); - priorityCodeValue = xrmApp.Entity.GetValue(priorityCode); - Assert.AreEqual(priorityCode.Value, priorityCodeValue); + string priorityCodeValue = _xrmApp.Entity.GetValue(priorityCode); + Assert.AreEqual(priorityCode.Value, priorityCodeValue); - xrmApp.ThinkTime(2000); - } + _xrmApp.ThinkTime(2000); } [TestMethod] - public void UCITestOpenCaseRetrieveHeaderValues_SetFields() + public void UCITestOpenCase_SetHeaderValues() { - var client = new WebClient(TestSettings.Options); - using (var xrmApp = new XrmApp(client)) - { - xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); + _xrmApp.Grid.SwitchView("Active Cases"); - xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); + _xrmApp.Grid.OpenRecord(0); - xrmApp.Navigation.OpenSubArea("Service", "Cases"); + LookupItem ownerId = new LookupItem {Name = "ownerid"}; + ownerId.Value = _xrmApp.Entity.GetHeaderValue(ownerId); + + _client.Browser.Driver.ClearFocus(); + + _xrmApp.Entity.SetHeaderValue(ownerId); - xrmApp.Grid.SwitchView("Active Cases"); - - xrmApp.Grid.OpenRecord(0); - - LookupItem ownerId = new LookupItem { Name = "ownerid" }; - string ownerIdValue = xrmApp.Entity.GetHeaderValue(ownerId); - - client.Browser.Driver.ClearFocus(); - - ownerId.Value = "Angel Rodriguez"; - xrmApp.Entity.SetHeaderValue(ownerId); - - ownerIdValue = xrmApp.Entity.GetHeaderValue(ownerId); - Assert.AreEqual(ownerId.Value, ownerIdValue); - - OptionSet priorityCode = new OptionSet { Name = "prioritycode" }; - string priorityCodeValue = xrmApp.Entity.GetHeaderValue(priorityCode); - - priorityCode.Value = "High"; - xrmApp.Entity.SetHeaderValue(priorityCode); - priorityCodeValue = xrmApp.Entity.GetHeaderValue(priorityCode); - Assert.AreEqual(priorityCode.Value, priorityCodeValue); - - xrmApp.ThinkTime(2000); - - } + var ownerIdValue = _xrmApp.Entity.GetHeaderValue(ownerId); + Assert.AreEqual(ownerId.Value, ownerIdValue); + OptionSet priorityCode = new OptionSet + { + Name = "prioritycode", + Value = "High" + }; + _xrmApp.Entity.SetHeaderValue(priorityCode); + string priorityCodeValue = _xrmApp.Entity.GetHeaderValue(priorityCode); + Assert.AreEqual(priorityCode.Value, priorityCodeValue); + + _xrmApp.ThinkTime(2000); } } } \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenContact.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenContact.cs index 8a52e796..2fc69a2c 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenContact.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenContact.cs @@ -22,10 +22,9 @@ public void UCITestOpenActiveContact() xrmApp.Navigation.OpenSubArea("Sales", "Contacts"); - xrmApp.Grid.Search("Anthony"); + xrmApp.Grid.Search("David"); xrmApp.Grid.OpenRecord(0); - } } @@ -41,6 +40,8 @@ public void UCITestOpenRecordSetNavigator() xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenSubArea("Sales", "Contacts"); + + xrmApp.Grid.SwitchView("Active Contacts"); xrmApp.Grid.OpenRecord(0); @@ -71,14 +72,17 @@ public void UCITestOpenSubGridRecord() xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenSubArea("Sales", "Contacts"); + + xrmApp.Grid.SwitchView("Active Contacts"); xrmApp.Grid.OpenRecord(0); - List rows = xrmApp.Entity.GetSubGridItems("RECENT OPPORTUNITIES"); + List rows = xrmApp.Entity.GetSubGridItems("Opportunities"); - int rowCount = xrmApp.Entity.GetSubGridItemsCount("RECENT CASES"); + int rowCount = xrmApp.Entity.GetSubGridItemsCount("Cases"); - xrmApp.Entity.OpenSubGridRecord("RECENT OPPORTUNITIES", 0); + if (rows.Count > 0) + xrmApp.Entity.OpenSubGridRecord("Opportunities", 0); xrmApp.ThinkTime(500); } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenLead.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenLead.cs index e8f99c50..b477449e 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenLead.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenLead.cs @@ -21,7 +21,7 @@ public void UCITestOpenActiveLead() xrmApp.Navigation.OpenSubArea("Sales", "Leads"); - xrmApp.Grid.Search("Linda"); + xrmApp.Grid.Search("David"); xrmApp.Grid.OpenRecord(0); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenOpportunity.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenOpportunity.cs index 0c4e5c62..1204865f 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenOpportunity.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Read/OpenOpportunity.cs @@ -21,12 +21,10 @@ public void UCITestOpenActiveOpportunity() xrmApp.Navigation.OpenSubArea("Sales", "Opportunities"); - xrmApp.Grid.Search("orb"); + xrmApp.Grid.Search("*Upgrade"); xrmApp.Grid.OpenRecord(0); - } - } [TestMethod] @@ -40,6 +38,8 @@ public void UCITestOpenOpportunityLookupAccount() xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenSubArea("Sales", "Opportunities"); + + xrmApp.Grid.SwitchView("Open Opportunities"); xrmApp.Grid.OpenRecord(0); @@ -63,6 +63,8 @@ public void UCITestOpenOpportunitySearchLookupAccount() xrmApp.Navigation.OpenApp(UCIAppName.Sales); xrmApp.Navigation.OpenSubArea("Sales", "Opportunities"); + + xrmApp.Grid.SwitchView("Open Opportunities"); xrmApp.Grid.OpenRecord(0); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/SetValue.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/SetValue.cs index 89e4c413..8e0b1ab9 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/SetValue.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/SetValue.cs @@ -91,7 +91,7 @@ public void UCITestActivityPartySetValue() xrmApp.ThinkTime(500); xrmApp.Entity.SetValue(new LookupItem[] { - new LookupItem { Name = "to", Value = "Adeventure Works (sample)", Index = 0 }, + new LookupItem { Name = "to", Value = "Adeventure Works", Index = 0 }, new LookupItem { Name = "to", Value = "", Index = 0 } }); xrmApp.ThinkTime(500); @@ -122,7 +122,7 @@ public void UCITestActivityPartyAddValues() xrmApp.ThinkTime(500); xrmApp.Entity.AddValues(new LookupItem[] { - new LookupItem { Name = "to", Value = "Adventure Works (sample)", Index = 0 }, + new LookupItem { Name = "to", Value = "Adventure Works", Index = 0 }, new LookupItem { Name = "to", Value = "", Index = 1 } }); xrmApp.ThinkTime(500); @@ -149,7 +149,7 @@ public void UCITestActivityPartyRemoveValues() xrmApp.ThinkTime(500); xrmApp.Entity.RemoveValues(new LookupItem[] { - new LookupItem { Name = "to", Value = "Adventure Works (sample)", Index = 0 }, + new LookupItem { Name = "to", Value = "Adventure Works", Index = 0 }, new LookupItem { Name = "to", Value = "", Index = 0 } }); xrmApp.ThinkTime(500); @@ -201,7 +201,7 @@ public void UCITestDateTimeSetValue() xrmApp.Entity.SetValue("name", "Test EasyRepro Opportunity"); - xrmApp.Entity.SetValue("estimatedclosedate", DateTime.Now, "M/d/yyyy h:mm tt"); + xrmApp.Entity.SetHeaderValue("estimatedclosedate", DateTime.Now); xrmApp.ThinkTime(500); xrmApp.Entity.Save(); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs index 10a40bd5..cb400917 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs @@ -18,6 +18,7 @@ public class TestsBase protected readonly bool _usePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"]); protected XrmApp _xrmApp; + protected WebClient _client; protected string _timed(string value) => $"{value} {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"; public virtual void InitTest() @@ -31,25 +32,46 @@ public virtual void FinishTest() CloseApp(); } - public void CreateApp(bool privateMode = true) + public XrmApp CreateApp(BrowserOptions options = null) { - BrowserOptions options = TestSettings.Options; - options.PrivateMode = privateMode || _usePrivateMode; - options.UCIPerformanceMode = false; + options = options ?? TestSettings.Options; + SetOptions(options); - var client = new WebClient(options); - _xrmApp = new XrmApp(client); + _client = new WebClient(options); + _xrmApp = new XrmApp(_client); _xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); - _xrmApp.Navigation.OpenApp(UCIAppName.Sales); + + return _xrmApp; } public void CloseApp() { _xrmApp.Dispose(); _xrmApp = null; + _client = null; + } + + public virtual void SetOptions(BrowserOptions options) + { + options.PrivateMode = _usePrivateMode; + options.UCIPerformanceMode = false; } - public virtual void NavigateToHomePage() { } + public virtual void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); + + public virtual void NavigateTo(string appName, string area = null, string subarea = null) + { + _xrmApp.Navigation.OpenApp(appName); + + var hasArea = !string.IsNullOrWhiteSpace(area); + var hasSubArea = !string.IsNullOrWhiteSpace(subarea); + if(hasArea && hasSubArea) + _xrmApp.Navigation.OpenSubArea(area, subarea); + else if(hasArea) + _xrmApp.Navigation.OpenArea(area); + else if(hasSubArea) + _xrmApp.Navigation.OpenSubArea(subarea); + } } } \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Update/UpdateLead.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Update/UpdateLead.cs index 8cf7f33e..ee2c275d 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Update/UpdateLead.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Update/UpdateLead.cs @@ -49,7 +49,7 @@ public void UCITestOpenActiveLeadBPF() LookupItem acct = new LookupItem(); acct.Name = "parentaccountid"; - acct.Value = "Adventure Works (sample)"; + acct.Value = "Adventure Works"; LookupItem contact = new LookupItem(); contact.Name = "parentcontactid"; diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/Web/BusinessProcessFlow/BusinessProcessFlow.cs b/Microsoft.Dynamics365.UIAutomation.Sample/Web/BusinessProcessFlow/BusinessProcessFlow.cs index f97f3076..20f64a6f 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/Web/BusinessProcessFlow/BusinessProcessFlow.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/Web/BusinessProcessFlow/BusinessProcessFlow.cs @@ -185,7 +185,7 @@ public void WEBTestLeadToOpportunityBPFClearAndSetValue() xrmBrowser.ThinkTime(2000); - xrmBrowser.BusinessProcessFlow.SetValue(new LookupItem { Name = "parentaccountid", Value = "Adventure Works (sample)" }); + xrmBrowser.BusinessProcessFlow.SetValue(new LookupItem { Name = "parentaccountid", Value = "Adventure Works" }); xrmBrowser.ThinkTime(2000); From 128d8353c2d2c65edf493886decfe3db5106447a Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Mon, 10 Feb 2020 13:28:14 +0100 Subject: [PATCH 03/12] Fix #752 do not try to Enter OTC if there is not Secrect Key --- .../WebClient.cs | 190 +++++++++--------- .../Extensions/RelativePositions.cs | 47 +++++ .../Extensions/SeleniumExtensions.cs | 55 ++--- ...ft.Dynamics365.UIAutomation.Browser.csproj | 1 + .../UCI/GetValue.cs | 3 + .../UCI/TestsBase.cs | 8 +- 6 files changed, 164 insertions(+), 140 deletions(-) create mode 100644 Microsoft.Dynamics365.UIAutomation.Browser/Extensions/RelativePositions.cs diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs index b5fb4279..db50431c 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs @@ -67,37 +67,23 @@ internal BrowserCommandResult InitializeModes() public string[] OnlineDomains { get; set; } #region PageWaits - - internal void WaitForLoginPage() - { - IWebDriver driver = this.Browser.Driver; - - driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]) - , TimeSpan.FromSeconds(60), - e => - { - //determine if we landed on the Unified Client Main page - if (driver.HasElement(By.XPath(Elements.Xpath[Reference.Login.CrmUCIMainPage]))) - { - driver.WaitForPageToLoad(); - driver.WaitForTransaction(); - } - else //else we landed on the Web Client main page or app picker page - SwitchToDefaultContent(driver); - }, - "Login page failed." - ); - } - - internal void WaitForMainPage() - { - IWebDriver driver = this.Browser.Driver; - driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage])); - driver.WaitForPageToLoad(); - if (driver.HasElement(By.XPath(Elements.Xpath[Reference.Login.CrmUCIMainPage]))) - { - driver.WaitForTransaction(); - } + internal bool WaitForMainPage(TimeSpan timeout, string errorMessage) + => WaitForMainPage(timeout, null, () => Browser.Driver.Throw(errorMessage)); + + internal bool WaitForMainPage(TimeSpan? timeout = null, Action successCallback = null, Action failureCallback = null) + { + IWebDriver driver = Browser.Driver; + timeout = timeout ?? Constants.DefaultTimeout; + successCallback = successCallback ?? ( + _ => { + bool isUCI = driver.HasElement(By.XPath(Elements.Xpath[Reference.Login.CrmUCIMainPage])); + if (isUCI) + driver.WaitForTransaction(); + }); + + var xpathToMainPage = By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]); + var element = driver.WaitUntilVisible(xpathToMainPage, timeout, successCallback, failureCallback); + return element != null; } #endregion @@ -133,7 +119,7 @@ private LoginResult Login(IWebDriver driver, Uri uri, SecureString username, Sec bool success = EnterUserName(driver, username); if (!success) { - var isUserAlreadyLogged = IsUserAlreadyLogged(driver); + var isUserAlreadyLogged = IsUserAlreadyLogged(); if (isUserAlreadyLogged) { SwitchToDefaultContent(driver); @@ -166,21 +152,23 @@ private LoginResult Login(IWebDriver driver, Uri uri, SecureString username, Sec ThinkTime(1000); } - EnterOneTimeCode(driver, mfaSecrectKey); - - ClickStaySignedIn(driver); - - ThinkTime(1000); + int attempts = 0; + bool entered; + do + { + entered = EnterOneTimeCode(driver, mfaSecrectKey); + success = ClickStaySignedIn(driver) || IsUserAlreadyLogged(); + attempts++; + } + while (!success && attempts <= Constants.DefaultRetryAttempts); // retry to enter the otc-code, if its fail & it is requested again + + if (entered && !success) + throw driver.Throw("Somethig got wrong entering the OTC. Please check the MFA-SecrectKey in configuration."); - return LoginResult.Success; + return success ? LoginResult.Success : LoginResult.Failure; } - private static bool IsUserAlreadyLogged(IWebDriver driver) - { - var xpathToMainPage = By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]); - bool result = driver.HasElement(xpathToMainPage); - return result; - } + private bool IsUserAlreadyLogged() => WaitForMainPage(2.Seconds()); private static string GenerateOneTimeCode(SecureString mfaSecrectKey) { @@ -214,41 +202,39 @@ private static void EnterPassword(IWebDriver driver, SecureString password) input.Submit(); } - private void EnterOneTimeCode(IWebDriver driver, SecureString mfaSecrectKey) + private bool EnterOneTimeCode(IWebDriver driver, SecureString mfaSecrectKey) { - int attempts = 0; - while (true) + try { - try - { - IWebElement input = GetOtcInput(driver); - if (input != null) - { - var oneTimeCode = GenerateOneTimeCode(mfaSecrectKey); - input.SendKeys(oneTimeCode); - input.Submit(); - return; - } - } - catch (Exception e) - { - Trace.TraceInformation($"An Error ocur entering OTC. Attempt {attempts} of {Constants.DefaultRetryAttempts}. Exception: {e}"); - if (attempts >= Constants.DefaultRetryAttempts) - throw; - } + IWebElement input = GetOtcInput(driver); // wait for the dialog, even if key is null, to print the right error + if (input == null) + return true; - attempts++; - ThinkTime(Constants.DefaultRetryDelay); + if (mfaSecrectKey == null) + throw driver.Throw("The application is wait for the OTC but your MFA-SecrectKey is not set. Please check your configuration."); + + var oneTimeCode = GenerateOneTimeCode(mfaSecrectKey); + SetInputValue(driver, input, oneTimeCode, 1.Seconds()); + input.Submit(); + return true; // input found & code was entered + } + catch (Exception e) + { + var message = $"An Error occur entering OTC. Exception: {e.Message}"; + Trace.TraceInformation(message); + throw driver.Throw(message, e); } } + private static IWebElement GetOtcInput(IWebDriver driver) => driver.WaitUntilAvailable(By.XPath(Elements.Xpath[Reference.Login.OneTimeCode]), TimeSpan.FromSeconds(2)); - private static void ClickStaySignedIn(IWebDriver driver) + private static bool ClickStaySignedIn(IWebDriver driver) { var xpath = By.XPath(Elements.Xpath[Reference.Login.StaySignedIn]); - driver.ClickWhenAvailable(xpath, TimeSpan.FromSeconds(10)); + var element = driver.ClickIfVisible(xpath, 5.Seconds()); + return element != null; } private static void SwitchToDefaultContent(IWebDriver driver) @@ -272,7 +258,22 @@ internal BrowserCommandResult PassThroughLogin(Uri uri) { driver.Navigate().GoToUrl(uri); - WaitForLoginPage(); + WaitForMainPage(60.Seconds(), + _ => + { + //determine if we landed on the Unified Client Main page + var isUCI = driver.HasElement(By.XPath(Elements.Xpath[Reference.Login.CrmUCIMainPage])); + if (isUCI) + { + driver.WaitForPageToLoad(); + driver.WaitForTransaction(); + } + else + //else we landed on the Web Client main page or app picker page + SwitchToDefaultContent(driver); + }, + () => driver.Throw("Load Main Page Fail.") + ); return LoginResult.Success; }); @@ -294,7 +295,7 @@ public void ADFSLoginAction(LoginRedirectEventArgs args) //Insert any additional code as required for the SSO scenario //Wait for CRM Page to load - driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]), TimeSpan.FromSeconds(60), "Login page failed."); + WaitForMainPage(TimeSpan.FromSeconds(60), "Login page failed."); SwitchToMainFrame(driver); } @@ -324,7 +325,7 @@ public void MSFTLoginAction(LoginRedirectEventArgs args) //Insert any additional code as required for the SSO scenario //Wait for CRM Page to load - driver.WaitUntilVisible(By.XPath(Elements.Xpath[Reference.Login.CrmMainPage]), TimeSpan.FromSeconds(60), "Login page failed."); + WaitForMainPage(TimeSpan.FromSeconds(60), "Login page failed."); SwitchToMainFrame(driver); } @@ -342,7 +343,7 @@ internal BrowserCommandResult OpenApp(string appName, int thinkTime = Cons driver.SwitchTo().DefaultContent(); //Handle left hand Nav in Web Client - var success = TryToClickInAppTile(appName, driver) || + var success = TryToClickInAppTile(appName, driver) || TryOpenAppFromMenu(driver, appName, AppReference.Navigation.WebAppMenuButton) || TryOpenAppFromMenu(driver, appName, AppReference.Navigation.UCIAppMenuButton); @@ -361,7 +362,8 @@ private bool TryOpenAppFromMenu(IWebDriver driver, string appName, string appMen bool found = false; var xpathToAppMenu = By.XPath(AppElements.Xpath[appMenuButton]); driver.WaitUntilClickable(xpathToAppMenu, TimeSpan.FromSeconds(5), - appMenu => { + appMenu => + { appMenu.Click(true); OpenAppFromMenu(driver, appName); found = true; @@ -373,7 +375,7 @@ internal void OpenAppFromMenu(IWebDriver driver, string appName) { var container = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Navigation.AppMenuContainer])); var xpathToButton = "//nav[@aria-hidden='false']//button//*[text()='[TEXT]']".Replace("[TEXT]", appName); - container.ClickWhenAvailable(By.XPath(xpathToButton), + container.ClickWhenAvailable(By.XPath(xpathToButton), TimeSpan.FromSeconds(1), $"App Name {appName} not found." ); @@ -406,10 +408,10 @@ private static bool TryToClickInAppTile(string appName, IWebDriver driver) var xpathToAppContainer = By.XPath(AppElements.Xpath[AppReference.Navigation.UCIAppContainer]); var xpathToappTile = By.XPath(AppElements.Xpath[AppReference.Navigation.UCIAppTile].Replace("[NAME]", appName)); - + bool success = false; - driver.WaitUntilVisible(xpathToAppContainer, TimeSpan.FromSeconds(5), - appContainer => success = appContainer.ClickWhenAvailable(xpathToappTile, TimeSpan.FromSeconds(5)) != null + driver.WaitUntilVisible(xpathToAppContainer, TimeSpan.FromSeconds(5), + appContainer => success = appContainer.ClickWhenAvailable(xpathToappTile, TimeSpan.FromSeconds(5)) != null ); return success; @@ -1034,7 +1036,7 @@ private static ICollection GetListItems(IWebElement container, Look { var name = control.Name; var xpathToItems = By.XPath(AppElements.Xpath[AppReference.Entity.LookupFieldResultListItem].Replace("[NAME]", name)); - + //wait for complete the search container.WaitUntil(d => d.FindVisible(By.XPath("//li/div/label/span"))?.Text?.Equals(control.Value, StringComparison.OrdinalIgnoreCase) == true); @@ -1135,10 +1137,10 @@ internal BrowserCommandResult> GetCommandValues(bool includeMoreCom private static List TryGetCommandValues(bool includeMoreCommandsValues, IWebDriver driver) { const string moreCommandsLabel = "more commands"; - + //Find the button in the CommandBar IWebElement ribbon = GetRibbon(driver); - + //Get the CommandBar buttons Dictionary commandBarItems = GetMenuItems(ribbon); bool hasMoreCommands = commandBarItems.TryGetValue(moreCommandsLabel, out var moreCommandsButton); @@ -1155,7 +1157,7 @@ private static List TryGetCommandValues(bool includeMoreCommandsValues, var result = GetCommandNames(commandBarItems.Values); return result; } - + private static Dictionary GetMenuItems(IWebElement menu) { var result = new Dictionary(); @@ -1186,8 +1188,8 @@ private static IWebElement GetRibbon(IWebDriver driver) var xpathCommandBarContainer = By.XPath(AppElements.Xpath[AppReference.CommandBar.Container]); var xpathCommandBarGrid = By.XPath(AppElements.Xpath[AppReference.CommandBar.ContainerGrid]); - IWebElement ribbon = - driver.WaitUntilAvailable(xpathCommandBarContainer, 5.Seconds()) ?? + IWebElement ribbon = + driver.WaitUntilAvailable(xpathCommandBarContainer, 5.Seconds()) ?? driver.WaitUntilAvailable(xpathCommandBarGrid, 5.Seconds()) ?? throw new InvalidOperationException("Unable to find the ribbon."); @@ -1220,7 +1222,7 @@ public BrowserCommandResult> OpenViewPicker(int continue; var key = viewItem.Text.ToLowerString(); - if(string.IsNullOrWhiteSpace(key)) + if (string.IsNullOrWhiteSpace(key)) continue; if (!result.ContainsKey(key)) @@ -1255,7 +1257,7 @@ internal BrowserCommandResult OpenRecord(int index, int thinkTime = Consta { var xpathToGrid = By.XPath(AppElements.Xpath[AppReference.Grid.Container]); IWebElement control = driver.WaitUntilAvailable(xpathToGrid); - + Func action; if (checkRecord) action = e => e.Click(); @@ -1837,18 +1839,18 @@ internal BrowserCommandResult SetValue(string field, string value) }); } - private void SetInputValue(IWebDriver driver, IWebElement input, string value) + private void SetInputValue(IWebDriver driver, IWebElement input, string value, TimeSpan? thinktime = null) { input.SendKeys(Keys.Control + "a"); input.SendKeys(Keys.Backspace); driver.WaitForTransaction(); - if (!string.IsNullOrWhiteSpace(value)) - { - input.SendKeys(value, true); - driver.WaitForTransaction(); - ThinkTime(3000); - } + if (string.IsNullOrWhiteSpace(value)) + return; + + input.SendKeys(value, true); + driver.WaitForTransaction(); + ThinkTime(thinktime ?? 3.Seconds()); } /// @@ -1903,7 +1905,7 @@ internal BrowserCommandResult SetValue(LookupItem[] controls, bool clearFi TryRemoveLookupValue(driver, fieldContainer, control); TryToSetValue(fieldContainer, controls); - + return true; }); } @@ -1962,7 +1964,7 @@ private void SetLookUpByValue(ISearchContext container, LookupItem control) if (items.Count == 0) throw new InvalidOperationException($"List does not contain a record with the name: {control.Value}"); - + int index = control.Index; if (index >= items.Count) throw new InvalidOperationException($"List does not contain {index + 1} records. Please provide an index value less than {items.Count} "); diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/RelativePositions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/RelativePositions.cs new file mode 100644 index 00000000..27ffe1b1 --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/RelativePositions.cs @@ -0,0 +1,47 @@ +// Created by: Rodriguez Mustelier Angel (rodang) +// Modify On: 2020-02-10 11:26 + +using System; +using System.Drawing; +using OpenQA.Selenium; + +public static class RelativePositions +{ + public static Func Above(this IWebElement outer, IWebElement inner) => + () => + { + int x = outer.Size.Width / 2; + int y = (inner.Location.Y - outer.Location.Y) / 2; + return new Point(x, y); + }; + + public static Func Below(this IWebElement outer, IWebElement inner) => + () => + { + int x = outer.Size.Width / 2; + var outerEnd = outer.Location + outer.Size; + var innerEnd = inner.Location + inner.Size; + int dBelow = outerEnd.Y - innerEnd.Y; + int y = innerEnd.Y + dBelow / 2; + return new Point(x, y); + }; + + public static Func LeftTo(this IWebElement outer, IWebElement inner) => + () => + { + int x = (inner.Location.X - outer.Location.X) / 2; + int y = outer.Size.Height / 2; + return new Point(x, y); + }; + + public static Func RightTo(this IWebElement outer, IWebElement inner) => + () => + { + var outerEnd = outer.Location + outer.Size; + var innerEnd = inner.Location + inner.Size; + int dRight = outerEnd.X - innerEnd.X; + int x = innerEnd.X + dRight / 2; ; + int y = outer.Size.Height / 2; + return new Point(x, y); + }; +} \ 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 e7042cb8..5a36fecd 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs @@ -626,6 +626,18 @@ public static bool RepeatUntil(this IWebDriver driver, Action action, Predicate< #endregion Waits #region Args / Tracing + + public static Exception Throw(this IWebDriver driver, string message, Exception innerException = null) + { + driver.Quit(); + return new InvalidOperationException(message, innerException); + } + + public static Exception Throw(this IWebDriver driver, string message, Exception innerException = null) where T : Exception + { + driver.Quit(); + return (T) Activator.CreateInstance(typeof(T), message, innerException); + } public static string ToTraceString(this FindElementEventArgs e) { @@ -648,45 +660,4 @@ public static string ToTraceString(this FindElementEventArgs e) #endregion Args / Tracing } -} - -public static class RelativePositions -{ - public static Func Above(this IWebElement outer, IWebElement inner) => - () => - { - int x = outer.Size.Width / 2; - int y = (inner.Location.Y - outer.Location.Y) / 2; - return new Point(x, y); - }; - - public static Func Below(this IWebElement outer, IWebElement inner) => - () => - { - int x = outer.Size.Width / 2; - var outerEnd = outer.Location + outer.Size; - var innerEnd = inner.Location + inner.Size; - int dBelow = outerEnd.Y - innerEnd.Y; - int y = innerEnd.Y + dBelow / 2; - return new Point(x, y); - }; - - public static Func LeftTo(this IWebElement outer, IWebElement inner) => - () => - { - int x = (inner.Location.X - outer.Location.X) / 2; - int y = outer.Size.Height / 2; - return new Point(x, y); - }; - - public static Func RightTo(this IWebElement outer, IWebElement inner) => - () => - { - var outerEnd = outer.Location + outer.Size; - var innerEnd = inner.Location + inner.Size; - int dRight = outerEnd.X - innerEnd.X; - int x = innerEnd.X + dRight / 2; ; - int y = outer.Size.Height / 2; - return new Point(x, y); - }; -} +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj b/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj index e8422a04..d601f616 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Microsoft.Dynamics365.UIAutomation.Browser.csproj @@ -78,6 +78,7 @@ + diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs index e0bc71ef..0bb2caca 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GetValue.cs @@ -23,6 +23,7 @@ public void UCITestGetValueFromOptionSet() { _xrmApp.Navigation.OpenSubArea("Contacts"); + _xrmApp.Grid.SwitchView("Active Contacts"); _xrmApp.Grid.OpenRecord(0); string option = _xrmApp.Entity.GetValue(new OptionSet {Name = "preferredcontactmethodcode"}); @@ -34,6 +35,8 @@ public void UCITestGetValueFromLookup() { _xrmApp.Navigation.OpenSubArea("Accounts"); + _xrmApp.Grid.SwitchView("Active Accounts"); + _xrmApp.Grid.OpenRecord(0); _xrmApp.ThinkTime(2000); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs index cb400917..144faf36 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs @@ -12,10 +12,10 @@ namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI public class TestsBase { protected readonly Uri _xrmUri = new Uri(ConfigurationManager.AppSettings["OnlineCrmUrl"]); - protected readonly SecureString _username = ConfigurationManager.AppSettings["OnlineUsername"].ToSecureString(); - protected readonly SecureString _password = ConfigurationManager.AppSettings["OnlinePassword"].ToSecureString(); - protected readonly SecureString _mfaSecrectKey = ConfigurationManager.AppSettings["MfaSecrectKey"].ToSecureString(); - protected readonly bool _usePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"]); + protected readonly SecureString _username = ConfigurationManager.AppSettings["OnlineUsername"]?.ToSecureString(); + protected readonly SecureString _password = ConfigurationManager.AppSettings["OnlinePassword"]?.ToSecureString(); + protected readonly SecureString _mfaSecrectKey = ConfigurationManager.AppSettings["MfaSecrectKey"]?.ToSecureString(); + protected readonly bool _usePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? bool.TrueString); protected XrmApp _xrmApp; protected WebClient _client; From f8720e70ea66b63ece7f294784eee48cb13d7874 Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Tue, 11 Feb 2020 22:15:26 +0100 Subject: [PATCH 04/12] Enable Set Privacy Mode for Edge --- Microsoft.Dynamics365.UIAutomation.Browser/BrowserOptions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/BrowserOptions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/BrowserOptions.cs index 7e8d6e43..c1cf3b58 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/BrowserOptions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/BrowserOptions.cs @@ -152,7 +152,8 @@ public virtual EdgeOptions ToEdge() { var options = new EdgeOptions() { - PageLoadStrategy = PageLoadStrategy.Normal + PageLoadStrategy = PageLoadStrategy.Normal, + UseInPrivateBrowsing = PrivateMode }; return options; From 8de713aa69150470aab1146efeba393fccad8368 Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Wed, 12 Feb 2020 03:22:54 +0100 Subject: [PATCH 05/12] Add Some Demo Samples to UCI.TestsBase --- .../Extensions/RelativePositions.cs | 78 +++++++++---------- .../Extensions/SeleniumExtensions.cs | 40 +++++----- ...oft.Dynamics365.UIAutomation.Sample.csproj | 8 ++ .../GlobalSearch/GlobalCategorizedSearch.cs | 1 - .../UCI/QuickStart/1_Demo_TestsClass.cs | 59 ++++++++++++++ ...stsBase_WorkForYou_ReadConfigParameters.cs | 51 ++++++++++++ ...Demo_Let_TestsBase_WorkForYou_CreateApp.cs | 41 ++++++++++ ...ou_CreateApp_AllTestsUsesDefaultOptions.cs | 40 ++++++++++ ...u_CreateApp_AllTestsUsesMyCustomOptions.cs | 43 ++++++++++ ..._CreateApp_NavigateToSameApp_InAllTests.cs | 36 +++++++++ ...CreateApp_NavigateToSameArea_InAllTests.cs | 33 ++++++++ .../UCI/QuickStart/QuickStart.cs | 37 +++++++++ .../UCI/TestsBase.cs | 17 +--- 13 files changed, 410 insertions(+), 74 deletions(-) create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/4_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesDefaultOptions.cs create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/5_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/6_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/7_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameArea_InAllTests.cs create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/QuickStart.cs diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/RelativePositions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/RelativePositions.cs index 27ffe1b1..0865300b 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/RelativePositions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/RelativePositions.cs @@ -1,47 +1,47 @@ -// Created by: Rodriguez Mustelier Angel (rodang) -// Modify On: 2020-02-10 11:26 - -using System; +using System; using System.Drawing; using OpenQA.Selenium; -public static class RelativePositions +namespace Microsoft.Dynamics365.UIAutomation.Browser { - public static Func Above(this IWebElement outer, IWebElement inner) => - () => - { - int x = outer.Size.Width / 2; - int y = (inner.Location.Y - outer.Location.Y) / 2; - return new Point(x, y); - }; + public static class RelativePositions + { + public static Func Above(this IWebElement outer, IWebElement inner) => + () => + { + int x = outer.Size.Width / 2; + int y = (inner.Location.Y - outer.Location.Y) / 2; + return new Point(x, y); + }; - public static Func Below(this IWebElement outer, IWebElement inner) => - () => - { - int x = outer.Size.Width / 2; - var outerEnd = outer.Location + outer.Size; - var innerEnd = inner.Location + inner.Size; - int dBelow = outerEnd.Y - innerEnd.Y; - int y = innerEnd.Y + dBelow / 2; - return new Point(x, y); - }; + public static Func Below(this IWebElement outer, IWebElement inner) => + () => + { + int x = outer.Size.Width / 2; + var outerEnd = outer.Location + outer.Size; + var innerEnd = inner.Location + inner.Size; + int dBelow = outerEnd.Y - innerEnd.Y; + int y = innerEnd.Y + dBelow / 2; + return new Point(x, y); + }; - public static Func LeftTo(this IWebElement outer, IWebElement inner) => - () => - { - int x = (inner.Location.X - outer.Location.X) / 2; - int y = outer.Size.Height / 2; - return new Point(x, y); - }; + public static Func LeftTo(this IWebElement outer, IWebElement inner) => + () => + { + int x = (inner.Location.X - outer.Location.X) / 2; + int y = outer.Size.Height / 2; + return new Point(x, y); + }; - public static Func RightTo(this IWebElement outer, IWebElement inner) => - () => - { - var outerEnd = outer.Location + outer.Size; - var innerEnd = inner.Location + inner.Size; - int dRight = outerEnd.X - innerEnd.X; - int x = innerEnd.X + dRight / 2; ; - int y = outer.Size.Height / 2; - return new Point(x, y); - }; + public static Func RightTo(this IWebElement outer, IWebElement inner) => + () => + { + var outerEnd = outer.Location + outer.Size; + var innerEnd = inner.Location + inner.Size; + int dRight = outerEnd.X - innerEnd.X; + int x = innerEnd.X + dRight / 2; + int y = outer.Size.Height / 2; + return new Point(x, y); + }; + } } \ 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 5a36fecd..8bbcb637 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs @@ -1,7 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; using OpenQA.Selenium; using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Support.Events; @@ -40,6 +37,7 @@ public static IWebElement ClickIfVisible(this ISearchContext driver, By by, Time public static IWebElement ClickWhenAvailable(this ISearchContext driver, By by, TimeSpan? timeout = null, string errorMessage = null) => WaitUntilClickable(driver, by, timeout, e => e.Click(), errorMessage ?? "Unable to click element."); + public static IWebElement ClickWhenAvailable(this ISearchContext driver, By by, string errorMessage) => WaitUntilClickable(driver, by, null, e => e.Click(), errorMessage ?? "Unable to click element."); @@ -50,7 +48,7 @@ public static IWebElement ClickAndWait(this IWebDriver driver, By by, TimeSpan t return null; element.Click(); - System.Threading.Thread.Sleep((int)timeout.TotalMilliseconds); + System.Threading.Thread.Sleep((int) timeout.TotalMilliseconds); return element; } @@ -78,15 +76,15 @@ public static void Click(this IWebDriver driver, IWebElement element, Func offsetFunc = null, bool ignoreStaleElementException = true) => driver.Perform(a => a.DoubleClick(), element, offsetFunc, ignoreStaleElementException); - + public static void Perform(this IWebDriver driver, Func action, IWebElement element, Func offsetFunc = null, bool ignoreStaleElementException = true) { try - { + { var actions = new Actions(driver); - if(offsetFunc == null) - actions = actions.MoveToElement(element); - else + if (offsetFunc == null) + actions = actions.MoveToElement(element); + else { var offset = offsetFunc(); actions = actions.MoveToElement(element, offset.X, offset.Y); @@ -145,7 +143,7 @@ public static T GetJsonObject(this IWebDriver driver, string @object) var results = ExecuteScript(driver, $"return JSON.stringify({@object});").ToString(); var jsSerializer = new JavaScriptSerializer(); - jsSerializer.RegisterConverters(new[] { new DynamicJsonConverter() }); + jsSerializer.RegisterConverters(new[] {new DynamicJsonConverter()}); var jsonObj = new JavaScriptSerializer().Deserialize(results); @@ -217,7 +215,7 @@ public static bool HasAttribute(this IWebElement element, string attributeName) public static T GetAttribute(this IWebElement element, string attributeName) { string value = element.GetAttribute(attributeName) ?? string.Empty; - return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(value); + return (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(value); } public static string GetAuthority(this IWebDriver driver) @@ -291,7 +289,7 @@ public static bool WaitForPageToLoad(this IWebDriver driver, TimeSpan? timeout = { try { - state = ((IJavaScriptExecutor)driver).ExecuteScript(@"return document.readyState").ToString(); + state = ((IJavaScriptExecutor) driver).ExecuteScript(@"return document.readyState").ToString(); } catch (InvalidOperationException) { @@ -326,7 +324,7 @@ public static bool WaitForPageToLoad(this IWebDriver driver, TimeSpan? timeout = driver.SwitchTo().Window(driver.WindowHandles[0]); } - state = ((IJavaScriptExecutor)driver).ExecuteScript(@"return document.readyState").ToString(); + state = ((IJavaScriptExecutor) driver).ExecuteScript(@"return document.readyState").ToString(); if (!(state.Equals("complete", StringComparison.InvariantCultureIgnoreCase) || state.Equals("loaded", StringComparison.InvariantCultureIgnoreCase))) throw; } @@ -342,7 +340,7 @@ public static bool WaitForTransaction(this IWebDriver driver, TimeSpan? timeout wait.IgnoreExceptionTypes(typeof(TimeoutException), typeof(NullReferenceException)); try { - state = wait.Until(d => (bool)driver.ExecuteScript("return window.UCWorkBlockTracker.isAppIdle()")); // Check to see if UCI is idle + state = wait.Until(d => (bool) driver.ExecuteScript("return window.UCWorkBlockTracker.isAppIdle()")); // Check to see if UCI is idle } catch (Exception) { @@ -523,7 +521,7 @@ public static bool WaitUntil(this ISearchContext driver, Predicate(driver) { Timeout = timeout ?? Constants.DefaultTimeout }; + var wait = new DefaultWait(driver) {Timeout = timeout ?? Constants.DefaultTimeout}; wait.IgnoreExceptionTypes(typeof(NoSuchElementException), typeof(StaleElementReferenceException)); bool success = false; @@ -569,13 +567,13 @@ public static IWebElement WaitUntil(this ISearchContext driver, Func WaitUntil(this ISearchContext driver, Func> searchFunc, + + public static ICollection WaitUntil(this ISearchContext driver, Func> searchFunc, TimeSpan? timeout = null, Action> successCallback = null, Action failureCallback = null) { ICollection elements = null; - Predicate condition = d => + Predicate condition = d => { elements = searchFunc(d); return elements.Count > 0; @@ -589,7 +587,7 @@ public static ICollection WaitUntil(this ISearchContext driver, Fu return elements; } - + public static bool RepeatUntil(this IWebDriver driver, Action action, Predicate predicate, TimeSpan? timeout = null, int attemps = Constants.DefaultRetryAttempts, @@ -626,7 +624,7 @@ public static bool RepeatUntil(this IWebDriver driver, Action action, Predicate< #endregion Waits #region Args / Tracing - + public static Exception Throw(this IWebDriver driver, string message, Exception innerException = null) { driver.Quit(); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/Microsoft.Dynamics365.UIAutomation.Sample.csproj b/Microsoft.Dynamics365.UIAutomation.Sample/Microsoft.Dynamics365.UIAutomation.Sample.csproj index 2b503ebf..43b47abb 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/Microsoft.Dynamics365.UIAutomation.Sample.csproj +++ b/Microsoft.Dynamics365.UIAutomation.Sample/Microsoft.Dynamics365.UIAutomation.Sample.csproj @@ -102,6 +102,14 @@ + + + + + + + + diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GlobalSearch/GlobalCategorizedSearch.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GlobalSearch/GlobalCategorizedSearch.cs index bbf9b53a..b562cc18 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GlobalSearch/GlobalCategorizedSearch.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/GlobalSearch/GlobalCategorizedSearch.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using Microsoft.VisualStudio.TestTools.UnitTesting; -using Microsoft.Dynamics365.UIAutomation.Api.UCI; namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI { diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs new file mode 100644 index 00000000..72e1aef6 --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs @@ -0,0 +1,59 @@ +using System; +using System.Configuration; +using System.Security; +using Microsoft.Dynamics365.UIAutomation.Api.UCI; +using Microsoft.Dynamics365.UIAutomation.Browser; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI +{ + [TestClass] + public class Demo_TestsClass { + + readonly Uri _xrmUri = new Uri(ConfigurationManager.AppSettings["OnlineCrmUrl"]); + readonly SecureString _username = ConfigurationManager.AppSettings["OnlineUsername"]?.ToSecureString(); + readonly SecureString _password = ConfigurationManager.AppSettings["OnlinePassword"]?.ToSecureString(); + readonly SecureString _mfaSecrectKey = ConfigurationManager.AppSettings["MfaSecrectKey"]?.ToSecureString(); + readonly bool _usePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? bool.TrueString); + + [TestMethod] + public void ThisTestDoNotUseTheBaseClass() + { + var options = TestSettings.Options; + options.PrivateMode = _usePrivateMode; + options.UCIPerformanceMode = false; // <= you can also change other settings here, for this tests only + + var client = new WebClient(options); + using (var xrmApp = new XrmApp(client)) + { + xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); // <= You can use different credentials here, but ensure to convert this ToSecureString + + xrmApp.Navigation.OpenApp(UCIAppName.Sales); // <= change this parameters to navigate to another app + + xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); // <= change this parameters to navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + + } // Note: that here get the Browser closed, xrmApp get disposed + } + + [TestMethod] + public void ThisTestDoNotUseTheBaseClass_GoingToCases_InCustomerServicesApp() + { + var options = TestSettings.Options; + options.PrivateMode = false; // <= this test is not in private mode, ignore config + + var client = new WebClient(options); + using (var xrmApp = new XrmApp(client)) + { + xrmApp.OnlineLogin.Login(_xrmUri, "anton@contoso.com".ToSecureString(), "2xTanTan!".ToSecureString(), "WhereIsMyKey?".ToSecureString()); // <= this tests use other credentials, ignore config + + xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); // <= navigate to another app + + xrmApp.Navigation.OpenSubArea("Service", "Cases"); // <= navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs new file mode 100644 index 00000000..2ef91cbb --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs @@ -0,0 +1,51 @@ +using Microsoft.Dynamics365.UIAutomation.Api.UCI; +using Microsoft.Dynamics365.UIAutomation.Browser; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI +{ + [TestClass] + public class Demo_Let_TestsBase_WorkForYou_ReadConfigParameters : TestsBase { + + // Read Config Parameters are now defined in TestsBase everything else still the same + + [TestMethod] + public void ThisTestDoNotUseTheBaseClass() + { + var options = TestSettings.Options; + options.UCIPerformanceMode = false; // <= you can also change other settings here + + var client = new WebClient(options); + using (var xrmApp = new XrmApp(client)) + { + xrmApp.OnlineLogin.Login(_xrmUri, _username, _password, _mfaSecrectKey); // <= this are now comming from TestsBase, but you can change it for some specific test + + xrmApp.Navigation.OpenApp(UCIAppName.Sales); // <= change this parameters to navigate to another app + + xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); // <= change this parameters to navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + + } // Note: that here get the Browser closed, xrmApp get disposed + } + + [TestMethod] + public void ThisTestDoNotUseTheBaseClass_GoingToCases_InCustomerServicesApp() + { + var options = TestSettings.Options; + options.PrivateMode = false; // <= this test is not in private mode, ignore config + + var client = new WebClient(options); + using (var xrmApp = new XrmApp(client)) + { + xrmApp.OnlineLogin.Login(_xrmUri, "anton@contoso.com".ToSecureString(), "2xTanTan!".ToSecureString(), "WhereIsMyKey?".ToSecureString()); // <= this tests use other credentials, ignore config + + xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); // <= navigate to another app + + xrmApp.Navigation.OpenSubArea("Service", "Cases"); // <= navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs new file mode 100644 index 00000000..03341b84 --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs @@ -0,0 +1,41 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI +{ + [TestClass] + public class Demo_Let_TestsBase_WorkForYou_CreateApp : TestsBase { + + [TestMethod] + public void ThisTestUseTheBaseClass() + { + var options = TestSettings.Options; + options.UCIPerformanceMode = false; // <= you can also change other settings here + + using (var xrmApp = CreateApp(options)) // <= CreateApp is now calling Login for you, with your options + { + xrmApp.Navigation.OpenApp(UCIAppName.Sales); // <= change this parameters to navigate to another app + + xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); // <= change this parameters to navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + + } // Note: that here get the Browser closed, xrmApp get disposed + } + + [TestMethod] + public void ThisTestUseTheBaseClass_GoingToCases_InCustomerServicesApp() + { + var options = TestSettings.Options; + using (var xrmApp = CreateApp(options)) + { + // now all tests uses the same credentials + + xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); // <= navigate to another app + + xrmApp.Navigation.OpenSubArea("Service", "Cases"); // <= navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/4_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesDefaultOptions.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/4_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesDefaultOptions.cs new file mode 100644 index 00000000..d93d0d72 --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/4_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesDefaultOptions.cs @@ -0,0 +1,40 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI +{ + [TestClass] + public class Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesDefaultOptions : TestsBase { + + [TestMethod] + public void UseTheBaseClass() + { + using (var xrmApp = CreateApp()) // <= CreateApp is now calling Login for you, with your options + { + xrmApp.Navigation.OpenApp(UCIAppName.Sales); // <= change this parameters to navigate to another app + + xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); // <= change this parameters to navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + + } // Note: that here get the Browser closed, CreateApp + } + + [TestMethod] + public void UseTheBaseClass_GoToCases_InCustomerServicesApp() + { + var options = TestSettings.Options; + options.PrivateMode = false; // <= this test still using my custom options + + using (var xrmApp = CreateApp(options)) + { + // now all tests uses the same credentials + + xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); // <= navigate to another app + + xrmApp.Navigation.OpenSubArea("Service", "Cases"); // <= navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/5_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/5_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs new file mode 100644 index 00000000..29e3df5a --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/5_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs @@ -0,0 +1,43 @@ +using Microsoft.Dynamics365.UIAutomation.Browser; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI +{ + [TestClass] + public class Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions : TestsBase + { + public override void SetOptions(BrowserOptions options) + { + // <= test in this class use my custom options + options.PrivateMode = false; + options.UCIPerformanceMode = false; + } + + [TestMethod] + public void ThisTestDoNotUseTheBaseClass() + { + using (var xrmApp = CreateApp()) // <= CreateApp is now calling Login for you, running with your options + { + xrmApp.Navigation.OpenApp(UCIAppName.Sales); // <= change this parameters to navigate to another app + + xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); // <= change this parameters to navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + + } // Note: that here get the Browser closed, CreateApp + } + + [TestMethod] + public void ThisTestDoNotUseTheBaseClass_GoingToCases_InCustomerServicesApp() + { + using (var xrmApp = CreateApp()) + { + xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); // <= navigate to another app + + xrmApp.Navigation.OpenSubArea("Service", "Cases"); // <= navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/6_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/6_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs new file mode 100644 index 00000000..79198a55 --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/6_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs @@ -0,0 +1,36 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI +{ + [TestClass] + public class Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests : TestsBase + { + [TestInitialize] + public override void InitTest() => base.InitTest(); // here is xrmApp initialized before each tests + + [TestCleanup] + public override void FinishTest() => base.FinishTest(); // <= here get Browser closed, xrmApp get disposed, after each tests + + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales); // => going to Sale Hub App + + [TestMethod] + public void ThisTestDoNotUseTheBaseClass() + { + // _xrmApp is now an instance variable, instead local + + _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); // <= change this parameters to navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + } + + [TestMethod] + public void ThisTestDoNotUseTheBaseClass_GoingToCases_InCustomerServicesApp() + { + // all test are going now to the same app + + _xrmApp.Navigation.OpenSubArea("Sales", "Opportunities"); // <= navigate to another area + + Assert.IsNotNull("Replace this line with your test code"); + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/7_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameArea_InAllTests.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/7_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameArea_InAllTests.cs new file mode 100644 index 00000000..1cd66381 --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/7_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameArea_InAllTests.cs @@ -0,0 +1,33 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI +{ + [TestClass] + public class Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameArea_InAllTests : TestsBase + { + [TestInitialize] + public override void InitTest() => base.InitTest(); // <= here is xrmApp initialized before each tests, Login inclusive + + [TestCleanup] + public override void FinishTest() => base.FinishTest(); // <= here get Browser closed, xrmApp get disposed, after each tests + + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); // => going to Sale Hub App, Sales Area, Accounts Sub Area + + [TestMethod] + public void TestCreateAccount() + { + // You can keep your focus in what is really important, TestsBase do the rest. + _xrmApp.CommandBar.ClickCommand("New"); + + Assert.IsNotNull("Replace this line with your test code"); + } + + [TestMethod] + public void TestOpenAccount() + { + _xrmApp.Grid.OpenRecord(0); + + Assert.IsNotNull("Replace this line with your test code"); + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/QuickStart.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/QuickStart.cs new file mode 100644 index 00000000..75d1ba1f --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/QuickStart.cs @@ -0,0 +1,37 @@ +using Microsoft.Dynamics365.UIAutomation.Browser; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI +{ + [TestClass] // NUnit => [TestFixture] + public class Demo_QuickStart : TestsBase + { + [TestInitialize] + public override void InitTest() => base.InitTest(); // here is _xrmApp initialized before each tests + + [TestCleanup] + public override void FinishTest() => base.FinishTest(); // <= // here get Browser closed, xrmApp get disposed, after each tests + + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); // => NavigateTo( App, Area, Sub Area ) in all tests + + // Optional: this will override the default parameters values & from this app.config + public override void SetOptions(BrowserOptions options) => options.UCIPerformanceMode = false; // => set options for all tests in this class + + [TestMethod] + public void TestCreateAccount() + { + // You can keep your focus in what is really important, TestsBase do the rest. + _xrmApp.CommandBar.ClickCommand("New"); + + Assert.IsNotNull("Replace this line with your test code"); + } + + [TestMethod] + public void TestOpenAccount() + { + _xrmApp.Grid.OpenRecord(0); + + Assert.IsNotNull("Replace this line with your test code"); + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs index 144faf36..f0f92767 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs @@ -1,7 +1,4 @@ -// Created by: Rodriguez Mustelier Angel (rodang) -// Modify On: 2020-01-23 02:51 - -using System; +using System; using System.Configuration; using System.Security; using Microsoft.Dynamics365.UIAutomation.Api.UCI; @@ -15,12 +12,10 @@ public class TestsBase protected readonly SecureString _username = ConfigurationManager.AppSettings["OnlineUsername"]?.ToSecureString(); protected readonly SecureString _password = ConfigurationManager.AppSettings["OnlinePassword"]?.ToSecureString(); protected readonly SecureString _mfaSecrectKey = ConfigurationManager.AppSettings["MfaSecrectKey"]?.ToSecureString(); - protected readonly bool _usePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? bool.TrueString); - + protected XrmApp _xrmApp; protected WebClient _client; - protected string _timed(string value) => $"{value} {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"; - + public virtual void InitTest() { CreateApp(); @@ -52,11 +47,7 @@ public void CloseApp() _client = null; } - public virtual void SetOptions(BrowserOptions options) - { - options.PrivateMode = _usePrivateMode; - options.UCIPerformanceMode = false; - } + public virtual void SetOptions(BrowserOptions options) { } public virtual void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); From 5f6248c3af09607e177e17ad4f4d609fba1e0b2c Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Wed, 12 Feb 2020 03:32:20 +0100 Subject: [PATCH 06/12] Add Some Demo Samples: how to use UCI.TestsBase --- Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs b/Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs index f2a17190..708c23bf 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/TestSettings.cs @@ -19,7 +19,7 @@ public static class TestSettings private static readonly string RemoteType = ConfigurationManager.AppSettings["RemoteBrowserType"]; private static readonly string RemoteHubServerURL = ConfigurationManager.AppSettings["RemoteHubServer"]; private static readonly string DriversPath = ConfigurationManager.AppSettings["DriversPath"] ?? string.Empty; - private static readonly bool UsePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? "true"); + private static readonly bool UsePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? bool.TrueString); // Once you change this instance will affect all follow tests executions public static BrowserOptions SharedOptions = new BrowserOptions From a3344d503fd5c0d6d3a9a5f94533afef83b09beb Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Wed, 12 Feb 2020 04:33:32 +0100 Subject: [PATCH 07/12] Add Some Demo Samples: how to use UCI.TestsBase --- .../UCI/QuickStart/1_Demo_TestsClass.cs | 4 ++-- .../2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs | 4 ++-- .../QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs | 2 +- ...tsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs | 4 ++-- ...sBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs index 72e1aef6..1e4db797 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs @@ -17,7 +17,7 @@ public class Demo_TestsClass { readonly bool _usePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? bool.TrueString); [TestMethod] - public void ThisTestDoNotUseTheBaseClass() + public void DoNotUseTheBaseClass() { var options = TestSettings.Options; options.PrivateMode = _usePrivateMode; @@ -38,7 +38,7 @@ public void ThisTestDoNotUseTheBaseClass() } [TestMethod] - public void ThisTestDoNotUseTheBaseClass_GoingToCases_InCustomerServicesApp() + public void DoNotUseTheBaseClass_GoToCases_InCustomerServicesApp() { var options = TestSettings.Options; options.PrivateMode = false; // <= this test is not in private mode, ignore config diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs index 2ef91cbb..d6a22046 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs @@ -10,7 +10,7 @@ public class Demo_Let_TestsBase_WorkForYou_ReadConfigParameters : TestsBase { // Read Config Parameters are now defined in TestsBase everything else still the same [TestMethod] - public void ThisTestDoNotUseTheBaseClass() + public void UseTheBaseClass() { var options = TestSettings.Options; options.UCIPerformanceMode = false; // <= you can also change other settings here @@ -30,7 +30,7 @@ public void ThisTestDoNotUseTheBaseClass() } [TestMethod] - public void ThisTestDoNotUseTheBaseClass_GoingToCases_InCustomerServicesApp() + public void UseTheBaseClass_GoToCases_InCustomerServicesApp() { var options = TestSettings.Options; options.PrivateMode = false; // <= this test is not in private mode, ignore config diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs index 03341b84..e5d717b2 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs @@ -23,7 +23,7 @@ public void ThisTestUseTheBaseClass() } [TestMethod] - public void ThisTestUseTheBaseClass_GoingToCases_InCustomerServicesApp() + public void ThisTestUseTheBaseClass_GoToCases_InCustomerServicesApp() { var options = TestSettings.Options; using (var xrmApp = CreateApp(options)) diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/5_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/5_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs index 29e3df5a..8ab969a0 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/5_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/5_Demo_Let_TestsBase_WorkForYou_CreateApp_AllTestsUsesMyCustomOptions.cs @@ -14,7 +14,7 @@ public override void SetOptions(BrowserOptions options) } [TestMethod] - public void ThisTestDoNotUseTheBaseClass() + public void UseTheBaseClass() { using (var xrmApp = CreateApp()) // <= CreateApp is now calling Login for you, running with your options { @@ -28,7 +28,7 @@ public void ThisTestDoNotUseTheBaseClass() } [TestMethod] - public void ThisTestDoNotUseTheBaseClass_GoingToCases_InCustomerServicesApp() + public void UseTheBaseClass_GoToCases_InCustomerServicesApp() { using (var xrmApp = CreateApp()) { diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/6_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/6_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs index 79198a55..1e21f88e 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/6_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/6_Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTests.cs @@ -14,7 +14,7 @@ public class Demo_Let_TestsBase_WorkForYou_CreateApp_NavigateToSameApp_InAllTest public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales); // => going to Sale Hub App [TestMethod] - public void ThisTestDoNotUseTheBaseClass() + public void UseTheBaseClass() { // _xrmApp is now an instance variable, instead local @@ -24,7 +24,7 @@ public void ThisTestDoNotUseTheBaseClass() } [TestMethod] - public void ThisTestDoNotUseTheBaseClass_GoingToCases_InCustomerServicesApp() + public void UseTheBaseClass_GoToCases_InCustomerServicesApp() { // all test are going now to the same app From c9ff50dbdd31d108ecb7bb42c19c57e42f49b92b Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Wed, 12 Feb 2020 12:41:27 +0100 Subject: [PATCH 08/12] Add Some Demo Samples: how to use UCI.TestsBase : Ignore Exception in Demo Tests when Login intentional fail --- .../UCI/QuickStart/1_Demo_TestsClass.cs | 12 ++++++------ ..._Let_TestsBase_WorkForYou_ReadConfigParameters.cs | 7 ++++--- .../3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs | 4 ++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs index 1e4db797..4289c031 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/1_Demo_TestsClass.cs @@ -14,13 +14,12 @@ public class Demo_TestsClass { readonly SecureString _username = ConfigurationManager.AppSettings["OnlineUsername"]?.ToSecureString(); readonly SecureString _password = ConfigurationManager.AppSettings["OnlinePassword"]?.ToSecureString(); readonly SecureString _mfaSecrectKey = ConfigurationManager.AppSettings["MfaSecrectKey"]?.ToSecureString(); - readonly bool _usePrivateMode = Convert.ToBoolean(ConfigurationManager.AppSettings["UsePrivateMode"] ?? bool.TrueString); [TestMethod] - public void DoNotUseTheBaseClass() + public void I_Hate_TheBaseClass() { var options = TestSettings.Options; - options.PrivateMode = _usePrivateMode; + options.PrivateMode = true; options.UCIPerformanceMode = false; // <= you can also change other settings here, for this tests only var client = new WebClient(options); @@ -37,8 +36,8 @@ public void DoNotUseTheBaseClass() } // Note: that here get the Browser closed, xrmApp get disposed } - [TestMethod] - public void DoNotUseTheBaseClass_GoToCases_InCustomerServicesApp() + [TestMethod, ExpectedException(typeof(Exception), AllowDerivedTypes = true)] + public void I_Hate_TheBaseClass_GoToCases_InCustomerServicesApp() { var options = TestSettings.Options; options.PrivateMode = false; // <= this test is not in private mode, ignore config @@ -46,7 +45,8 @@ public void DoNotUseTheBaseClass_GoToCases_InCustomerServicesApp() var client = new WebClient(options); using (var xrmApp = new XrmApp(client)) { - xrmApp.OnlineLogin.Login(_xrmUri, "anton@contoso.com".ToSecureString(), "2xTanTan!".ToSecureString(), "WhereIsMyKey?".ToSecureString()); // <= this tests use other credentials, ignore config + + xrmApp.OnlineLogin.Login(_xrmUri, "anton@contoso.com".ToSecureString(), "2xTanTan!".ToSecureString(), "WhereIsMySecrectKey?".ToSecureString()); // <= this tests use other credentials, ignore config xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); // <= navigate to another app diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs index d6a22046..6068c59d 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/2_Demo_Let_TestsBase_WorkForYou_ReadConfigParameters.cs @@ -1,4 +1,5 @@ -using Microsoft.Dynamics365.UIAutomation.Api.UCI; +using System; +using Microsoft.Dynamics365.UIAutomation.Api.UCI; using Microsoft.Dynamics365.UIAutomation.Browser; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -29,7 +30,7 @@ public void UseTheBaseClass() } // Note: that here get the Browser closed, xrmApp get disposed } - [TestMethod] + [TestMethod, ExpectedException(typeof(Exception), AllowDerivedTypes = true)] public void UseTheBaseClass_GoToCases_InCustomerServicesApp() { var options = TestSettings.Options; @@ -38,7 +39,7 @@ public void UseTheBaseClass_GoToCases_InCustomerServicesApp() var client = new WebClient(options); using (var xrmApp = new XrmApp(client)) { - xrmApp.OnlineLogin.Login(_xrmUri, "anton@contoso.com".ToSecureString(), "2xTanTan!".ToSecureString(), "WhereIsMyKey?".ToSecureString()); // <= this tests use other credentials, ignore config + xrmApp.OnlineLogin.Login(_xrmUri, "anton@contoso.com".ToSecureString(), "2xTanTan!".ToSecureString(), "WhereIsMySecrectKey?".ToSecureString()); // <= this tests use other credentials, ignore config xrmApp.Navigation.OpenApp(UCIAppName.CustomerService); // <= navigate to another app diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs index e5d717b2..7ef9e30c 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/QuickStart/3_Demo_Let_TestsBase_WorkForYou_CreateApp.cs @@ -6,7 +6,7 @@ namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI public class Demo_Let_TestsBase_WorkForYou_CreateApp : TestsBase { [TestMethod] - public void ThisTestUseTheBaseClass() + public void UseTheBaseClass() { var options = TestSettings.Options; options.UCIPerformanceMode = false; // <= you can also change other settings here @@ -23,7 +23,7 @@ public void ThisTestUseTheBaseClass() } [TestMethod] - public void ThisTestUseTheBaseClass_GoToCases_InCustomerServicesApp() + public void UseTheBaseClass_GoToCases_InCustomerServicesApp() { var options = TestSettings.Options; using (var xrmApp = CreateApp(options)) From 29af917cee7ff051b9564d98583f059f5e4c01ac Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Fri, 14 Feb 2020 00:20:53 +0100 Subject: [PATCH 09/12] Fix: Navigation Sitemap Button Change => TestInitialize Fail => Browser do not get closed (Fix previous Issue that was solved with driver.Throw <= remove this) --- .../DTO/AppElementReference.cs | 2 +- .../WebClient.cs | 10 ++-- .../Extensions/SeleniumExtensions.cs | 12 ----- ...oft.Dynamics365.UIAutomation.Sample.csproj | 1 + .../UCI/ApiTests/TestsBase_Tests.cs | 51 +++++++++++++++++++ .../UCI/CommandBar/DuplicateDetection.cs | 2 - .../UCI/Create/CreateAccount.cs | 7 ++- .../UCI/TestsBase.cs | 16 ++++-- 8 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 Microsoft.Dynamics365.UIAutomation.Sample/UCI/ApiTests/TestsBase_Tests.cs diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/DTO/AppElementReference.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/DTO/AppElementReference.cs index 278cd730..79c32a78 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/DTO/AppElementReference.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/DTO/AppElementReference.cs @@ -277,7 +277,7 @@ public static class AppElements { "Nav_WebAppMenuButton" , "//*[@id=\"TabArrowDivider\"]/a"}, { "Nav_UCIAppMenuButton" , "//button[@data-id=\"navbar-switch-app\"]"}, { "Nav_SiteMapLauncherButton", "//button[@data-lp-id=\"sitemap-launcher\"]" }, - { "Nav_SiteMapLauncherCloseButton", "//button[@aria-label=\"Close Site Map\"]" }, + { "Nav_SiteMapLauncherCloseButton", "//button[@data-id='navbutton']" }, { "Nav_SiteMapAreaMoreButton", "//button[@data-lp-id=\"sitemap-areaBar-more-btn\"]" }, { "Nav_SiteMapSingleArea", "//li[translate(@data-text,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz') = '[NAME]']" }, { "Nav_AppMenuContainer" , "//*[@id=\"taskpane-scroll-container\"]"}, diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs index db50431c..ccc4d61b 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/WebClient.cs @@ -68,7 +68,7 @@ internal BrowserCommandResult InitializeModes() #region PageWaits internal bool WaitForMainPage(TimeSpan timeout, string errorMessage) - => WaitForMainPage(timeout, null, () => Browser.Driver.Throw(errorMessage)); + => WaitForMainPage(timeout, null, () => throw new InvalidOperationException(errorMessage)); internal bool WaitForMainPage(TimeSpan? timeout = null, Action successCallback = null, Action failureCallback = null) { @@ -163,7 +163,7 @@ private LoginResult Login(IWebDriver driver, Uri uri, SecureString username, Sec while (!success && attempts <= Constants.DefaultRetryAttempts); // retry to enter the otc-code, if its fail & it is requested again if (entered && !success) - throw driver.Throw("Somethig got wrong entering the OTC. Please check the MFA-SecrectKey in configuration."); + throw new InvalidOperationException("Somethig got wrong entering the OTC. Please check the MFA-SecrectKey in configuration."); return success ? LoginResult.Success : LoginResult.Failure; } @@ -211,7 +211,7 @@ private bool EnterOneTimeCode(IWebDriver driver, SecureString mfaSecrectKey) return true; if (mfaSecrectKey == null) - throw driver.Throw("The application is wait for the OTC but your MFA-SecrectKey is not set. Please check your configuration."); + throw new InvalidOperationException("The application is wait for the OTC but your MFA-SecrectKey is not set. Please check your configuration."); var oneTimeCode = GenerateOneTimeCode(mfaSecrectKey); SetInputValue(driver, input, oneTimeCode, 1.Seconds()); @@ -222,7 +222,7 @@ private bool EnterOneTimeCode(IWebDriver driver, SecureString mfaSecrectKey) { var message = $"An Error occur entering OTC. Exception: {e.Message}"; Trace.TraceInformation(message); - throw driver.Throw(message, e); + throw new InvalidOperationException(message, e); } } @@ -272,7 +272,7 @@ internal BrowserCommandResult PassThroughLogin(Uri uri) //else we landed on the Web Client main page or app picker page SwitchToDefaultContent(driver); }, - () => driver.Throw("Load Main Page Fail.") + () => new InvalidOperationException("Load Main Page Fail.") ); return LoginResult.Success; diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs index 8bbcb637..704db28a 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs @@ -625,18 +625,6 @@ public static bool RepeatUntil(this IWebDriver driver, Action action, Predicate< #region Args / Tracing - public static Exception Throw(this IWebDriver driver, string message, Exception innerException = null) - { - driver.Quit(); - return new InvalidOperationException(message, innerException); - } - - public static Exception Throw(this IWebDriver driver, string message, Exception innerException = null) where T : Exception - { - driver.Quit(); - return (T) Activator.CreateInstance(typeof(T), message, innerException); - } - public static string ToTraceString(this FindElementEventArgs e) { var method = e.FindMethod.ToString(); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/Microsoft.Dynamics365.UIAutomation.Sample.csproj b/Microsoft.Dynamics365.UIAutomation.Sample/Microsoft.Dynamics365.UIAutomation.Sample.csproj index 43b47abb..499114fd 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/Microsoft.Dynamics365.UIAutomation.Sample.csproj +++ b/Microsoft.Dynamics365.UIAutomation.Sample/Microsoft.Dynamics365.UIAutomation.Sample.csproj @@ -88,6 +88,7 @@ + diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/ApiTests/TestsBase_Tests.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/ApiTests/TestsBase_Tests.cs new file mode 100644 index 00000000..5b19a209 --- /dev/null +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/ApiTests/TestsBase_Tests.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI.ApiTests +{ + [TestClass] + public class TestsBase_TestInitializeFail : TestsBase + { + public override void NavigateToHomePage() + { + // Assert that CreateApp was success + Assert.IsNotNull(_client); + Assert.IsNotNull(_xrmApp); + + throw new Exception("Simulating Navigation Fail => Browser should be closed & the exception rethrow"); + } + + [TestMethod, ExpectedException(typeof(Exception))] + public void NavigationFail_ExceptionIsRethrow() + { + // Simulate => [TestInitialize] + base.InitTest(); + + Assert.Fail("If Exception is rethrow. This code should never get executed"); + + // Simulate => [TestCleanup] + // base.FinishTest(); + } + + [TestMethod] + public void NavigationFail_BrowserIsClosed() + { + try + { + // Simulate => [TestInitialize] + base.InitTest(); + + Assert.Fail("If Exception is rethrow. This code should never get executed"); + } + catch + { + // Assert that CloseApp was called + Assert.IsNull(_xrmApp, "Browser still open"); + Assert.IsNull(_client, "Browser still open"); + } + } + } +} \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/DuplicateDetection.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/DuplicateDetection.cs index 67ed2c38..640c4e29 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/DuplicateDetection.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/DuplicateDetection.cs @@ -19,8 +19,6 @@ public class DuplicateDetection : TestsBase [TestMethod] public void UCITestDuplicateDetection() { - _xrmApp.Navigation.OpenSubArea("Sales", "Contacts"); - _xrmApp.CommandBar.ClickCommand("New"); _xrmApp.Entity.SetValue("firstname", "EasyRepro"); diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs index 8e10cb45..8935a5ed 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Microsoft.Dynamics365.UIAutomation.Sample.UCI @@ -14,18 +15,16 @@ public class CreateAccount : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); + public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); [TestMethod] public void UCITestCreateAccount() { - _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); - _xrmApp.CommandBar.ClickCommand("New"); _xrmApp.Entity.SetValue("name", TestSettings.GetRandomString(5, 15)); - _xrmApp.Entity.Save(); + _xrmApp.Entity.Save(); } } } \ No newline at end of file diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs index f0f92767..60e6311e 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/TestsBase.cs @@ -18,8 +18,16 @@ public class TestsBase public virtual void InitTest() { - CreateApp(); - NavigateToHomePage(); + try + { + CreateApp(); + NavigateToHomePage(); + } + catch + { + CloseApp(); + throw; + } } public virtual void FinishTest() @@ -42,14 +50,14 @@ public XrmApp CreateApp(BrowserOptions options = null) public void CloseApp() { - _xrmApp.Dispose(); + _xrmApp?.Dispose(); _xrmApp = null; _client = null; } public virtual void SetOptions(BrowserOptions options) { } - public virtual void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); + public virtual void NavigateToHomePage() {} public virtual void NavigateTo(string appName, string area = null, string subarea = null) { From 49715cbbf06d817b206984aefd303165a2869c06 Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Mon, 17 Feb 2020 16:32:01 +0100 Subject: [PATCH 10/12] Minor Fixes --- .../Elements/OnlineLogin.cs | 6 +++--- .../Extensions/SeleniumExtensions.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/OnlineLogin.cs b/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/OnlineLogin.cs index 770b6734..2f00568c 100644 --- a/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/OnlineLogin.cs +++ b/Microsoft.Dynamics365.UIAutomation.Api.UCI/Elements/OnlineLogin.cs @@ -23,7 +23,7 @@ public void Login(Uri orgUrl) { _client.Login(orgUrl); - //_client.InitializeModes(); + _client.InitializeModes(); } /// @@ -37,7 +37,7 @@ public void Login(Uri orgUrl, SecureString username, SecureString password, Secu { _client.Login(orgUrl, username, password, mfaSecrectKey); - //_client.InitializeModes(); + _client.InitializeModes(); } /// @@ -52,7 +52,7 @@ public void Login(Uri orgUrl, SecureString username, SecureString password, Secu { _client.Login(orgUrl, username, password, mfaSecrectKey, redirectAction); - //_client.InitializeModes(); + _client.InitializeModes(); } } diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs index 704db28a..a3797e52 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs @@ -576,7 +576,7 @@ public static ICollection WaitUntil(this ISearchContext driver, Fun Predicate condition = d => { elements = searchFunc(d); - return elements.Count > 0; + return elements != null && elements.Count > 0; }; bool success = driver.WaitUntil(condition); From 7381921e82ea07a178bd757056dbfad59f54cbfa Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Mon, 17 Feb 2020 16:37:30 +0100 Subject: [PATCH 11/12] Restore Licence Comment --- .../Extensions/SeleniumExtensions.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs index a3797e52..d841d1ad 100644 --- a/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs +++ b/Microsoft.Dynamics365.UIAutomation.Browser/Extensions/SeleniumExtensions.cs @@ -1,4 +1,7 @@ -using Newtonsoft.Json.Linq; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Newtonsoft.Json.Linq; using OpenQA.Selenium; using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Support.Events; From fe441e8fd56a90830112d27fd155ac0c0b1354cd Mon Sep 17 00:00:00 2001 From: "Angel A. Rodriguez" Date: Mon, 17 Feb 2020 17:03:49 +0100 Subject: [PATCH 12/12] Remove useless calls to Navigate --- .../UCI/CommandBar/CommandButton.cs | 1 - .../UCI/Create/CreateAccount.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CommandButton.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CommandButton.cs index 586f8024..a642381e 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CommandButton.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/CommandBar/CommandButton.cs @@ -19,7 +19,6 @@ public class CommandButton : TestsBase [TestMethod] public void UCITestNewCommandBarButton() { - _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); _xrmApp.CommandBar.ClickCommand("New"); _xrmApp.ThinkTime(2000); } diff --git a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs index 8935a5ed..8af0f474 100644 --- a/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs +++ b/Microsoft.Dynamics365.UIAutomation.Sample/UCI/Create/CreateAccount.cs @@ -15,7 +15,7 @@ public class CreateAccount : TestsBase [TestCleanup] public override void FinishTest() => base.FinishTest(); - public override void NavigateToHomePage() => _xrmApp.Navigation.OpenSubArea("Sales", "Accounts"); + public override void NavigateToHomePage() => NavigateTo(UCIAppName.Sales, "Sales", "Accounts"); [TestMethod] public void UCITestCreateAccount()