public void Test_that_error_report_doesnt_break_the_app() { using var tmpDir = new TemporaryDirectory(); var path = Path.Combine(tmpDir.Path, "invalid.aasx"); File.WriteAllText(path, "totally invalid"); Common.RunWithMainWindow((application, automation, mainWindow) => { Common.AssertLoadAasx(application, mainWindow, path); var numberErrors = Retry.Find( () => (application.HasExited) ? null : mainWindow.FindFirstChild(cf => cf.ByAutomationId("LabelNumberErrors")), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5) }); Assert.AreEqual("Errors: 1", numberErrors.AsLabel().Text); var buttonReport = Retry.Find( () => mainWindow.FindFirstChild(cf => cf.ByAutomationId("ButtonReport")), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5), TimeoutMessage = "Could not find the report button" }).AsButton(); buttonReport.Click(); Common.RequireTopLevelWindowByTitle(application, automation, "Message Report"); }); }
public void Test_that_the_asset_image_is_displayed() { var path = Common.PathTo01FestoAasx(); Common.RunWithMainWindow((application, automation, mainWindow) => { Common.AssertLoadAasx(application, mainWindow, path); Common.AssertNoErrors(application, mainWindow); const string automationId = "AssetPic"; var assetPic = Retry.Find( () => mainWindow.FindFirstChild(cf => cf.ByAutomationId(automationId)), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5) }); Assert.IsNotNull(assetPic, $"Could not find the element: {automationId}"); // The dimensions of the image will not be set properly if the image could not be loaded. if (assetPic.BoundingRectangle.Height <= 1 || assetPic.BoundingRectangle.Width <= 1) { throw new AssertionException( "The asset picture has unexpected dimensions: " + $"width is {assetPic.BoundingRectangle.Width} and " + $"height is {assetPic.BoundingRectangle.Height}"); } }); }
public void Test_that_about_does_not_break_the_app() { Common.RunWithMainWindow((application, automation, mainWindow) => { var helpMenuItem = mainWindow .FindFirstDescendant( cf => cf.ByClassName("MenuItem").And(cf.ByName("Help"))) .AsMenuItem(); helpMenuItem.Click(); var aboutMenuItem = helpMenuItem .FindFirstChild(cf => cf.ByName("About ..")) .AsMenuItem(); aboutMenuItem.Click(); Retry.WhileNull(() => // ReSharper disable once AccessToDisposedClosure application.GetAllTopLevelWindows(automation) .FirstOrDefault((w) => w.Title == "About"), throwOnTimeout: true, timeout: TimeSpan.FromSeconds(5), timeoutMessage: "Could not find the about window" ); }); }
public static Window RequireTopLevelWindow(Application application, UIA3Automation automation, Func <Window, bool> filter, string timeoutMessage, int timeoutSeconds = 5) { return(Retry.WhileNull(() => // ReSharper disable once AccessToDisposedClosure application.GetAllTopLevelWindows(automation).FirstOrDefault(filter), throwOnTimeout: true, timeout: TimeSpan.FromSeconds(timeoutSeconds), timeoutMessage: timeoutMessage ).Result); }
public static void AssertLoadAasx(Application application, Window mainWindow, string path) { if (!File.Exists(path)) { throw new InvalidOperationException($"The AASX file to be loaded does not exist: {path}"); } var fileMenuItem = mainWindow .FindFirstDescendant( cf => cf.ByClassName("MenuItem").And(cf.ByName("File"))) .AsMenuItem(); fileMenuItem.Click(); var openMenuItem = fileMenuItem .FindFirstChild(cf => cf.ByName("Open ..")) .AsMenuItem(); if (openMenuItem == null) { throw new AssertionException( "The open menu item is null. You need to thoroughly inspect what happened -- " + "this is quite strange."); } openMenuItem.Click(); Retry.WhileEmpty( () => mainWindow.ModalWindows, throwOnTimeout: true, timeout: TimeSpan.FromSeconds(10), timeoutMessage: "Could not find the modal windows of the main window"); Assert.AreEqual(1, mainWindow.ModalWindows.Length); var modal = mainWindow.ModalWindows[0]; var pathCombo = modal.FindFirstChild(cf => cf.ByAutomationId("1148")).AsComboBox(); pathCombo.EditableText = path; var openButton = modal.FindFirstChild(cf => cf.ByAutomationId("1")).AsButton(); openButton.Click(); Assert.IsEmpty(modal.ModalWindows, $"Unexpected modal window (probably an error) while opening the AASX: {path}"); Retry.WhileTrue(() => mainWindow.ModalWindows.Length > 0, throwOnTimeout: true, timeout: TimeSpan.FromSeconds(10)); if (application.HasExited) { throw new AssertionException( "The application unexpectedly exited. " + $"Check manually why the file could not be opened: {path}"); } }
public void Test_that_splash_screen_does_not_break_the_app() { Common.RunWithMainWindow((application, automation, mainWindow) => { Retry.WhileNull(() => // ReSharper disable once AccessToDisposedClosure application.GetAllTopLevelWindows(automation) .FirstOrDefault((w) => w.AutomationId == "splashScreen"), throwOnTimeout: true, timeout: TimeSpan.FromSeconds(5), timeoutMessage: "Could not find the splash screen" ); Common.AssertNoErrors(application, mainWindow); }, new Run { Args = new[] { "-splash", "5000" } }); }
/// <summary> /// Retry until the label element with number of errors is found and then check that there are no errors. /// /// If the search times out, an exception will be thrown. /// </summary> /// <param name="application">AASX Package Explorer application under test</param> /// <param name="mainWindow">Main window of <paramref name="application"/></param> /// <remarks>Both <paramref name="application"/> and <paramref name="mainWindow"/> should be obtained /// with <see cref="RunWithMainWindow"/></remarks> public static void AssertNoErrors(Application application, Window mainWindow) { const string automationId = "LabelNumberErrors"; var numberErrors = Retry.Find( () => (application.HasExited) ? null : mainWindow.FindFirstChild(cf => cf.ByAutomationId(automationId)), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5) }); Assert.IsFalse(application.HasExited, "Application unexpectedly exited while searching for number of errors label"); Assert.IsNotNull(numberErrors, $"Element {automationId} could not be found."); Assert.AreEqual("Text", numberErrors.ClassName, $"Expected {automationId} to be a label"); Assert.AreEqual("No errors", numberErrors.AsLabel().Text, "Expected no errors on startup"); }
/// <summary> /// Finds the main AASX Package Explorer window and executes the code dependent on it. /// </summary> /// <remarks>This method is necessary since splash screen confuses FlaUI and prevents us from /// easily determining the main window.</remarks> /// <param name="implementation">Code to be executed</param> public static void RunWithMainWindow(Implementation implementation) { string environmentVariable = "AASX_PACKAGE_EXPLORER_RELEASE_DIR"; string releaseDir = System.Environment.GetEnvironmentVariable(environmentVariable); if (releaseDir == null) { throw new InvalidOperationException( $"Expected the environment variable to be set: {environmentVariable}; " + "otherwise we can not find binaries to be tested through functional tests."); } string pathToExe = Path.Combine(releaseDir, "AasxPackageExplorer.exe"); if (!File.Exists(pathToExe)) { throw new FileNotFoundException( "The executable of the AASX Package Explorer " + $"could not be found in the release directory: {pathToExe}; did you compile it properly before?"); } var app = Application.Launch(pathToExe); try { using (var automation = new UIA3Automation()) { // ReSharper disable once AccessToDisposedClosure Retry.WhileEmpty(() => app.GetAllTopLevelWindows(automation)); var mainWindow = app .GetAllTopLevelWindows(automation) .First((w) => w.Title == "AASX Package Explorer"); implementation(app, automation, mainWindow); } } finally { app.Kill(); } }
public void Test_that_opening_an_invalid_AASX_does_not_break_the_app() { using var tmpDir = new TemporaryDirectory(); var path = Path.Combine(tmpDir.Path, "invalid.aasx"); File.WriteAllText(path, "totally invalid"); Common.RunWithMainWindow((application, automation, mainWindow) => { Common.AssertLoadAasx(application, mainWindow, path); var numberErrors = Retry.Find( () => (application.HasExited) ? null : mainWindow.FindFirstChild(cf => cf.ByAutomationId("LabelNumberErrors")), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5) }); Assert.AreEqual("Errors: 1", numberErrors.AsLabel().Text); }); }
public void Test_that_error_report_doesnt_break_the_app() { using var tmpDir = new TemporaryDirectory(); var path = Path.Combine(tmpDir.Path, "invalid.aasx"); File.WriteAllText(path, "totally invalid"); Common.RunWithMainWindow((application, automation, mainWindow) => { Common.AssertLoadAasx(application, mainWindow, path); var numberErrors = Retry.Find( () => (application.HasExited) ? null : mainWindow.FindFirstChild(cf => cf.ByAutomationId("LabelNumberErrors")), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5) }); Assert.AreEqual("Errors: 1", numberErrors.AsLabel().Text); var buttonReport = Retry.Find( () => mainWindow.FindFirstChild(cf => cf.ByAutomationId("ButtonReport")), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5), TimeoutMessage = "Could not find the report button" }).AsButton(); buttonReport.Click(); Retry.WhileNull(() => // ReSharper disable once AccessToDisposedClosure application.GetAllTopLevelWindows(automation) .FirstOrDefault((w) => w.Title == "Message Report"), throwOnTimeout: true, timeout: TimeSpan.FromSeconds(5), timeoutMessage: "Could not find the 'Message Report' window"); }); }
public void Test_that_tree_doesnt_change() { var path = Common.PathTo01FestoAasx(); Common.RunWithMainWindow((application, automation, mainWindow) => { Common.AssertNoErrors(application, mainWindow); var tree = Retry.Find( () => mainWindow.FindFirstDescendant( cf => cf.ByAutomationId("treeViewInner")), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5), TimeoutMessage = "Could not find the treeViewInner tree" }).AsTree(); if (tree == null) { throw new AssertionException("tree unexpectedly null"); }
/// <summary> /// Finds the main AASX Package Explorer window and executes the code dependent on it. /// </summary> /// <remarks>This method is necessary since splash screen confuses FlaUI and prevents us from /// easily determining the main window.</remarks> /// <param name="implementation">Code to be executed</param> /// <param name="run">Run options. If null, a new run with default values is used</param> public static void RunWithMainWindow(Implementation implementation, Run?run = null) { string releaseDir = ReleaseDir(); string pathToExe = Path.Combine(releaseDir, "AasxPackageExplorer.exe"); if (!File.Exists(pathToExe)) { throw new FileNotFoundException( "The executable of the AASX Package Explorer " + $"could not be found in the release directory: {pathToExe}; did you compile it properly before?"); } var resolvedRun = run ?? new Run(); // See https://stackoverflow.com/questions/5510343/escape-command-line-arguments-in-c-sharp string joinedArgs = string.Join( " ", resolvedRun.Args .Select(arg => Regex.Replace(arg, @"(\\*)" + "\"", @"$1$1\" + "\""))); var psi = new ProcessStartInfo { FileName = pathToExe, Arguments = joinedArgs, RedirectStandardError = true, WorkingDirectory = releaseDir, UseShellExecute = false }; bool gotStderr = false; var process = new Process { StartInfo = psi }; try { process.ErrorDataReceived += (sender, e) => { if (!string.IsNullOrEmpty(e.Data)) { gotStderr = true; TestContext.Error.WriteLine(e.Data); } }; process.Start(); process.BeginErrorReadLine(); } catch (Exception) { TestContext.Error.WriteLine( $"Failed to launch the process: FileName: {psi.FileName}, " + $"Arguments: {psi.Arguments}, Working directory: {psi.WorkingDirectory}"); throw; } var app = new Application(process, false); try { using var automation = new UIA3Automation(); var mainWindow = Retry.Find(() => // ReSharper disable once AccessToDisposedClosure app.GetAllTopLevelWindows(automation) .FirstOrDefault( (w) => w.AutomationId == "mainWindow"), new RetrySettings { ThrowOnTimeout = true, Timeout = TimeSpan.FromSeconds(5), TimeoutMessage = "Could not find the main window" }).AsWindow(); implementation(app, automation, mainWindow); } finally { if (!resolvedRun.DontKill) { app.Kill(); } } if (gotStderr) { throw new AssertionException( "Unexpected writes to standard error. Please see the test context for more detail."); } }