public void GoBack() { Log.Comment("Going to the previous page..."); // The System Back button sometimes cannot be found at the first attempt, // or invoking it sometimes fails. Retry a few times. for (int retries = 0; ; retries++) { try { // If the process has closed prematurely, then there's no back button to press, // so we'll just no-op. if (Process.HasExited) { Log.Comment("Process already exited"); return; } Log.Comment("Invoking the back button..."); FindElement.ById <Button>("__GoBackInvoker").InvokeAndWait(); Log.Comment("Invoke successful."); // We're now exiting the page we were previously on, so everything has changed. As such, we should clear our // element cache, in order to ensure that we don't accidentally retrieve any stale UI objects. ElementCache.Clear(); // Successfully found and invoked the Back button. Exit the retry loop. break; } catch (Exception e) { string log = "Failed to find and invoke Back button. Error: " + e.ToString(); if (retries == 10) { Log.Error(log); DumpHelper.DumpFullContext(); throw; } else { Log.Warning(log); if (retries % 2 == 0) { Log.Comment("Clearing element cache which may be stale."); ElementCache.Clear(); } Log.Comment("Back button was not found. Trying again ({0}).", retries); Thread.Sleep(150); } } } }
// The value of 'testName' should match that which was used when // registering the test in TestInventory.cs in the test app project. public TestSetupHelper(ICollection <string> testNames, TestSetupHelperOptions options = null, bool shouldRestrictInnerFrameSize = true) { if (options == null) { options = new TestSetupHelperOptions(); } this.Options = options; // If a test crashes, it can take a little bit of time before we can // restart the app again especially if watson is collecting dumps. Adding a // delayed retry can help avoid the case where we might otherwise fail a slew of // tests that come after the one that crashes the app. var retryCount = 10; while (retryCount-- > 0) { var success = TestSingleRun(testNames, shouldRestrictInnerFrameSize); if (success) { break; // no need to retry } else { Log.Warning("Failed to setup test. pending retries: " + retryCount); if (retryCount > 0) { Log.Comment("Waiting before retry..."); TestEnvironment.ShouldRestartApplication = true; Task.Delay(5000); } else { Log.Error("Failed to set up test!"); try { DumpHelper.DumpFullContext(); } catch (Exception e) { Log.Error("Also failed to dump context because of an exception: {0}", e.ToString()); throw; } throw new InvalidOperationException("All attempts to set up test failed."); } } } }
// this function returns true for successful run and false for unsuccessful run // in case of false, the calling function will retry calling this function private bool TestSingleRun(ICollection <string> testNames, bool shouldRestrictInnerFrameSize) { PreTestSetup(); // We were hitting an issue in the lab where sometimes the very first click would fail to go through resulting in // test instability. We work around this by clicking on element when the app launches. if (!string.IsNullOrEmpty(Options.AutomationIdOfSafeItemToClick)) { var safeItemToClick = FindElement.ById(Options.AutomationIdOfSafeItemToClick); if (safeItemToClick == null) { string errorMessage = $"Cannot find object with automation id '{Options.AutomationIdOfSafeItemToClick}'"; Log.Warning(errorMessage); DumpHelper.DumpFullContext(); return(false); //retry needed } InputHelper.LeftClick(safeItemToClick); } foreach (string testName in testNames) { Log.Comment(testName + " initializing TestSetupHelper"); UIObject uiObject; if (!String.IsNullOrEmpty(Options.ClassNameOfNavigationItemToInvoke)) { uiObject = FindElement.ByNameAndClassName(testName, Options.ClassNameOfNavigationItemToInvoke); } else { uiObject = FindElement.ByName(testName); } if (uiObject == null) { string errorMessage = string.Format("Cannot find test page for: {0}.", testName); // We'll raise the error message first so the dump has proper context preceding it, // and will then throw it as an exception so we immediately cease execution. Log.Warning(errorMessage); DumpHelper.DumpFullContext(); return(false); //retry needed } // We're now entering a new test page, so everything has changed. As such, we should clear our // element cache in order to ensure that we don't accidentally retrieve any stale UI objects. ElementCache.Clear(); Log.Comment("Waiting until __TestContentLoadedCheckBox to be checked by test app."); CheckBox cb = new CheckBox(FindElement.ById("__TestContentLoadedCheckBox")); using (var waiter = cb.GetToggledWaiter()) { var testButton = new Button(uiObject); testButton.Invoke(); if (cb.ToggleState != ToggleState.On) { waiter.Wait(); } } Wait.ForIdle(); Log.Comment("__TestContentLoadedCheckBox checkbox checked, page has loaded"); SetInnerFrameInLabDimensions(shouldRestrictInnerFrameSize); OpenedTestPages++; } TestCleanupHelper.TestSetupHelperPendingDisposals++; return(true); //no retry needed, it is a success }
// The value of 'testName' should match that which was used when // registering the test in TestInventory.cs in the test app project. public TestSetupHelper(string testName, string languageOverride = "", bool attemptRestartOnDispose = true) { // If a test crashes, it can take a little bit of time before we can // restart the app again especially if watson is collecting dumps. Adding a // delayed retry can help avoid the case where we might otherwise fail a slew of // tests that come after the one that crashes the app. var retryCount = 10; while (retryCount-- > 0) { try { AttemptRestartOnDispose = attemptRestartOnDispose; bool restartedTestApp = false; Log.Comment(testName + " initializing TestSetupHelper"); if (TestEnvironment.ShouldRestartApplication) { ElementCache.Clear(); Wait.ResetIdleHelper(); TestEnvironment.ShouldRestartApplication = false; Log.Comment("Restarting application to ensure test stability..."); TestEnvironment.Application.Close(); restartedTestApp = true; } if (restartedTestApp || (TestEnvironment.Application.Process != null && TestEnvironment.Application.Process.HasExited)) { if (!restartedTestApp) { // If the test application process exited because something crashed, // we'll restart it in order to make sure that other tests aren't affected. Log.Comment("Application exited unexpectedly. Reinitializing..."); } ElementCache.Clear(); Wait.ResetIdleHelper(); #if USING_TAEF string deploymentDir = TestEnvironment.TestContext.TestDeploymentDir; #else // TestDeploymentDir doesn't exist on MSTest's TestContext. The only thing we use it for // is to install the AppX, and we install the AppX in other ways when using MSTest, so // we don't need it there. string deploymentDir = null; #endif TestEnvironment.Application.Initialize(true, deploymentDir); } ElementCache.Clear(); Wait.ForIdle(); // Before we navigate to the test page, we need to make sure that we've set the language to what was requested. var languageChooser = TryFindElement.ById("LanguageChooser"); // Sometimes TestSetupHelper is used to navigate off of a page other than the main page. In those circumstances, // we won't have a pseudo-loc check box on the page, which is fine - we can just skip this step when that's the case, // as we'll have already gone into whatever language we wanted previously. if (languageChooser != null) { ComboBox languageChooserComboBox = new ComboBox(languageChooser); if (!String.IsNullOrEmpty(languageOverride)) { languageChooserComboBox.SelectItemById(languageOverride); } } // We were hitting an issue in the lab where sometimes the very first click would fail to go through resulting in // test instability. We work around this by clicking on element when the app launches. var currentPageTextBlock = FindElement.ById("__CurrentPage"); if (currentPageTextBlock == null) { string errorMessage = "Cannot find __CurrentPage textblock"; Log.Error(errorMessage); DumpHelper.DumpFullContext(); throw new InvalidOperationException(errorMessage); } InputHelper.LeftClick(currentPageTextBlock); var uiObject = FindElement.ByNameAndClassName(testName, "Button"); if (uiObject == null) { string errorMessage = string.Format("Cannot find test page for: {0}.", testName); // We'll raise the error message first so the dump has proper context preceding it, // and will then throw it as an exception so we immediately cease execution. Log.Error(errorMessage); DumpHelper.DumpFullContext(); throw new InvalidOperationException(errorMessage); } // We're now entering a new test page, so everything has changed. As such, we should clear our // element cache in order to ensure that we don't accidentally retrieve any stale UI objects. ElementCache.Clear(); Log.Comment("Waiting until __TestContentLoadedCheckBox to be checked by test app."); CheckBox cb = new CheckBox(FindElement.ById("__TestContentLoadedCheckBox")); if (cb.ToggleState != ToggleState.On) { using (var waiter = cb.GetToggledWaiter()) { var testButton = new Button(uiObject); testButton.Invoke(); Wait.ForIdle(); waiter.Wait(); } } else { var testButton = new Button(uiObject); testButton.Invoke(); } Wait.ForIdle(); Log.Comment("__TestContentLoadedCheckBox checkbox checked, page has loaded"); TestCleanupHelper.TestSetupHelperPendingDisposals++; break; } catch { Log.Warning("Failed to setup test. pending retries: " + retryCount); if (retryCount > 0) { Log.Comment("Waiting before retry..."); TestEnvironment.ShouldRestartApplication = true; Task.Delay(5000); } else { Log.Error("Failed to set up test!"); try { DumpHelper.DumpFullContext(); } catch (Exception e) { Log.Error("Also failed to dump context because of an exception: {0}", e.ToString()); } throw; } } } }