private void TouchAndMoveOut(QueryEx element) { var rect = _app.WaitForElement(element).Single().Rect; _app.DragCoordinates(rect.X + 10, rect.Y + 10, rect.Right + 10, rect.Y + 10); }
[Timeout(700000)] // Adjust this timeout based on average test run duration public async Task RunRuntimeTests() { Run("SamplesApp.Samples.UnitTests.UnitTestsPage"); IAppQuery AllQuery(IAppQuery query) // .All() is not yet supported for wasm. => AppInitializer.GetLocalPlatform() == Platform.Browser ? query : query.All(); var runButton = new QueryEx(q => AllQuery(q).Marked("runButton")); var failedTestsCount = new QueryEx(q => AllQuery(q).Marked("failedTestCount")); var failedTests = new QueryEx(q => AllQuery(q).Marked("failedTests")); var runningState = new QueryEx(q => AllQuery(q).Marked("runningState")); var runTestCount = new QueryEx(q => AllQuery(q).Marked("runTestCount")); bool IsTestExecutionDone() => runningState.GetDependencyPropertyValue("Text")?.ToString().Equals("Finished", StringComparison.OrdinalIgnoreCase) ?? false; _app.WaitForElement(runButton); _app.FastTap(runButton); var lastChange = DateTimeOffset.Now; var lastValue = ""; while (DateTimeOffset.Now - lastChange < TestRunTimeout) { var newValue = runTestCount.GetDependencyPropertyValue("Text")?.ToString(); if (lastValue != newValue) { lastChange = DateTimeOffset.Now; } await Task.Delay(TimeSpan.FromSeconds(.5)); if (IsTestExecutionDone()) { break; } } if (!IsTestExecutionDone()) { Assert.Fail("A test run timed out"); } var count = failedTestsCount.GetDependencyPropertyValue("Text").ToString(); if (count != "0") { var tests = failedTests.GetDependencyPropertyValue <string>("Text") .Split(new char[] { '§' }, StringSplitOptions.RemoveEmptyEntries) .Select((x, i) => $"\t{i + 1}. {x}\n") .ToArray(); var details = _app.Marked("failedTestDetails").GetDependencyPropertyValue("Text"); Assert.Fail( $"{tests.Length} unit test(s) failed.\n\tFailing Tests:\n{string.Join("", tests)}\n\n---\n\tDetails:\n{details}"); } TakeScreenshot("Runtime Tests Results", ignoreInSnapshotCompare: true); }
[Timeout(500000)] // Adjust this timeout based on average test run duration public async Task RunBenchmarks() { Run("Benchmarks.Shared.Controls.BenchmarkDotNetTestsPage"); IAppQuery AllQuery(IAppQuery query) // .All() is not yet supported for wasm. => AppInitializer.GetLocalPlatform() == Platform.Browser ? query : query.All(); var runButton = new QueryEx(q => AllQuery(q).Marked("runButton")); var runStatus = new QueryEx(q => AllQuery(q).Marked("runStatus")); var runCount = new QueryEx(q => AllQuery(q).Marked("runCount")); var benchmarkControl = new QueryEx(q => AllQuery(q).Marked("benchmarkControl")); bool IsTestExecutionDone() { try { var text = runStatus.GetDependencyPropertyValue("Text")?.ToString(); var r2 = text?.Equals("Finished", StringComparison.OrdinalIgnoreCase) ?? false; Console.WriteLine($"IsTestExecutionDone: {text} {r2}"); return(r2); } catch { Console.WriteLine("Skip IsTestExecutionDone"); // Skip exceptions as they may be timeouts return(false); } } _app.WaitForElement(runButton); TakeScreenshot("Begin", ignoreInSnapshotCompare: true); _app.FastTap(runButton); var lastChange = DateTimeOffset.Now; var lastValue = ""; while (DateTimeOffset.Now - lastChange < TestRunTimeout) { try { if (IsTestExecutionDone()) { break; } var newValue = runCount.GetDependencyPropertyValue("Text")?.ToString(); if (lastValue != newValue) { Console.WriteLine($"Loop: Test changed now:{DateTimeOffset.Now} lastChange: {lastChange}"); lastChange = DateTimeOffset.Now; TakeScreenshot($"Run {newValue}", ignoreInSnapshotCompare: true); } } catch (Exception e) { // Skip exceptions as they may be timeouts } await Task.Delay(TimeSpan.FromSeconds(.5)); Console.WriteLine($"Loop: now:{DateTimeOffset.Now} lastChange: {lastChange}"); } if (!IsTestExecutionDone()) { Assert.Fail("A test run timed out"); } var finalFile = ArchiveResults(benchmarkControl); TestContext.AddTestAttachment(finalFile, "benchmark-results.zip"); TakeScreenshot("Runtime Tests Results", ignoreInSnapshotCompare: true); }
public ScrollContextTracker(QueryEx currentContext) { _previousContext = _findScrollContext; _findScrollContext = currentContext; }
/// <summary> /// Attempts to find an element matching the <see cref="to"/> query, scrolling UP if necessary /// </summary> /// <param name="app">The application</param> /// <param name="to">The query that indicates the element that is searched for"/></param> /// <param name="timeout">[Optionnal] An explicit value for the timeout. If not specified, the default timeout will be used.</param> /// <returns>A query pointing to the resulting element if it was found</returns> public static QueryEx FindUpwards(this IApp app, QueryEx to, TimeSpan?timeout = null) { return(app.FindInternal(to, direction: FindScrollDirection.Up, timeOut: timeout)); }
public void ValidateShape(string shapeName, PixelTolerance?tolerance = null) { Run("UITests.Windows_UI_Xaml_Shapes.Basic_Shapes", skipInitialScreenshot: true); var ctrl = new QueryEx(q => q.Marked("_basicShapesTestRoot")); var expectedDirectory = Path.Combine( TestContext.CurrentContext.TestDirectory, "Windows_UI_Xaml_Shapes/Basics_Shapes_Tests_EpectedResults"); var actualDirectory = Path.Combine( TestContext.CurrentContext.WorkDirectory, nameof(Windows_UI_Xaml_Shapes), nameof(Basics_Shapes_Tests), shapeName); tolerance = tolerance ?? (new PixelTolerance() .WithColor(132) // We are almost only trying to detect edges .WithOffset(3, 3, LocationToleranceKind.PerPixel) .Discrete(2)); var failures = new List <(string test, Exception error)>(); // To improve performance, we run all test for a given stretch at once. var testGroups = _tests .Where(t => t.StartsWith(shapeName)) .GroupBy(t => string.Join("_", t.Split(new[] { '_' }, 3, StringSplitOptions.RemoveEmptyEntries).Take(2))); foreach (var testGroup in testGroups) { ctrl.SetDependencyPropertyValue("RunTest", string.Join(";", testGroup)); _app.WaitFor(() => !string.IsNullOrWhiteSpace(ctrl.GetDependencyPropertyValue <string>("TestResult")), timeout: TimeSpan.FromMinutes(1)); var testResultsRaw = ctrl.GetDependencyPropertyValue <string>("TestResult"); var testResults = testResultsRaw .Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) .Select(line => line.Split(new[] { ';' }, 3, StringSplitOptions.RemoveEmptyEntries)) .Where(line => line.Length == 3) .ToDictionary( line => line[0], line => { var testName = line[0]; var isSuccess = line[1] == "SUCCESS"; var data = Convert.FromBase64String(line[2]); var target = Path .Combine(actualDirectory, testName + (isSuccess ? ".png" : ".txt")) .GetNormalizedLongPath(); var targetFile = new FileInfo(target); targetFile.Directory.Create(); File.WriteAllBytes(target, data); SetOptions(targetFile, new ScreenshotOptions { IgnoreInSnapshotCompare = true }); TestContext.AddTestAttachment(target, testName); return(isSuccess ? new Bitmap(new MemoryStream(Convert.FromBase64String(line[2]))) : new Exception(Encoding.UTF8.GetString(Convert.FromBase64String(line[2]))) as object); }); foreach (var test in testGroup) { try { var expected = new FileInfo(Path.Combine(expectedDirectory, $"{test}.png")); if (!expected.Exists) { Assert.Fail($"Expected screenshot does not exists ({expected.FullName})"); } if (!testResults.TryGetValue(test, out var testResult)) { Assert.Fail($"No test result for {test}."); } if (testResult is Exception error) { Assert.Fail($"Test failed: {error.Message}"); } using (var actual = (Bitmap)testResult) { var scale = _app.GetDisplayScreenScaling(); ImageAssert.AreAlmostEqual(expected, ImageAssert.FirstQuadrant, actual, ImageAssert.FirstQuadrant, scale, tolerance.Value); } } catch (Exception e) { Console.Error.WriteLine(e); // Ease debug while reading log from CI failures.Add((test, e)); } } } if (failures.Any()) { throw new AggregateException( $"Failed tests ({failures.Count} of {testGroups.Sum(g => g.Count())}):\r\n{string.Join("\r\n", failures.Select(t => t.test))}\r\n", failures.Select(t => t.error)); } else { Console.WriteLine($"All {testGroups.Sum(g => g.Count())} ran successfully."); } }
private void TapSomewhereInElement(QueryEx element) { var rect = _app.WaitForElement(element).Single().Rect; _app.TapCoordinates(rect.X + 10, rect.Y + 10); }
/// <summary> /// Attempts to find an element matching the <see cref="to"/> query, scrolling up on /// the element matching the <see cref="within"/> query if necessary /// </summary> /// <param name="app">The application</param> /// <param name="to">The query that indicates the element that is searched for within <see cref="within"/></param> /// <param name="within">The query that indicates the element where any scrolling required to reach the element will occur</param> /// <param name="timeout">[Optionnal] An explicit value for the timeout. If not specified, the default timeout will be used.</param> /// <returns>A query pointing to the resulting element if it was found</returns> public static QueryEx FindUpwardsWithin(this IApp app, Func <QueryEx, QueryEx> to, QueryEx within, TimeSpan?timeout = null) { return(app.FindUpwardsWithin(QueryExFactory.BlankQuery().Transform(to), within, timeout: timeout)); }
/// <summary> /// Attempts to find an element marked with the given value, scrolling up on /// the element matching the <see cref="within"/> query if necessary /// </summary> /// <param name="app">The application</param> /// <param name="marked">The mark of the element that is searched for within <see cref="within"/></param> /// <param name="within">The query that indicates the element where any scrolling required to reach the element will occur</param> /// <param name="timeout">[Optionnal] An explicit value for the timeout. If not specified, the default timeout will be used.</param> /// <returns>A query pointing to the resulting element if it was found</returns> public static QueryEx FindUpwardsWithin(this IApp app, string marked, QueryEx within, TimeSpan?timeout = null) { return(app.FindUpwardsWithin(q => q.Marked(marked), within)); }
/// <summary> /// Attempts to find an element matching the <see cref="to"/> query, scrolling down on /// the element matching the <see cref="within"/> query if necessary /// </summary> /// <param name="app">The application</param> /// <param name="to">The query transform that indicates the element that is searched for within <see cref="within"/></param> /// <param name="within">The query that indicates the element where any scrolling required to reach the element will occur</param> /// <param name="timeout">[Optionnal] An explicit value for the timeout. If not specified, the default timeout will be used.</param> /// <returns>A query pointing to the resulting element if it was found</returns> public static QueryEx FindWithin(this IApp app, Func <QueryEx, QueryEx> to, QueryEx within, TimeSpan?timeout = null) { var target = QueryExFactory.BlankQuery().Transform(to); return(app.FindInternal(target, within, timeOut: timeout)); }
public static QueryEx FindWithin(this IApp app, string marked, QueryEx within, TimeSpan?timeout = null) { return(app.FindInternal(QueryExFactory.BlankQuery().Marked(marked), within, timeOut: timeout)); }
/// <summary> /// Attempts to find an element matching the <see cref="to"/> query, scrolling down on /// the element matching the <see cref="within"/> query if necessary /// </summary> /// <param name="app">The application</param> /// <param name="to">The query that indicates the element that is searched for within <see cref="within"/></param> /// <param name="within">The query that indicates the element where any scrolling required to reach the element will occur</param> /// <param name="timeout">[Optionnal] An explicit value for the timeout. If not specified, the default timeout will be used.</param> /// <returns>A query pointing to the resulting element if it was found</returns> public static QueryEx FindWithin(this IApp app, QueryEx to, QueryEx within, TimeSpan?timeout = null) { return(app.FindInternal(to, within, timeOut: timeout)); }
// Uno specific private void TapAt(QueryEx query, double x, double y) { var firstResult = query.FirstResult(); _app.TapCoordinates((float)(firstResult.Rect.X + x), (float)(firstResult.Rect.Y + y)); }
private void ToggleTreeViewItemCheckBox(QueryEx item, int level) { var firstItem = item.FirstResult(); _app.TapCoordinates((float)(firstItem.Rect.X + firstItem.Rect.Width * 0.05 * level), (float)(firstItem.Rect.Y + firstItem.Rect.Height * .5)); }
private string GetText(QueryEx textBlock) => textBlock.GetDependencyPropertyValue <string>("Text");
internal static QueryEx FindInternal(this IApp app, QueryEx target, QueryEx within = null, FindScrollDirection direction = FindScrollDirection.DownThenUp, TimeSpan?timeOut = null) { // TODO: Add Visibility check here ? if (AttemptToFindTargetBeforeScrolling && target.HasResults()) { return(target); } Action executeScrollDownTo; Action executeScrollUpTo; if (within == null) { executeScrollDownTo = () => { app.ScrollDownTo( toQuery: target, strategy: DefaultScrollStrategy, swipeSpeed: InstantSwipeSpeed, withInertia: false, timeout: timeOut ); }; executeScrollUpTo = () => { app.ScrollUpTo( toQuery: target, strategy: DefaultScrollStrategy, swipeSpeed: InstantSwipeSpeed, withInertia: false, timeout: timeOut ); }; } else { executeScrollDownTo = () => { app.ScrollDownTo( toQuery: target, withinQuery: within, strategy: DefaultScrollStrategy, swipeSpeed: InstantSwipeSpeed, withInertia: false, timeout: timeOut ); }; executeScrollUpTo = () => { app.ScrollUpTo( toQuery: target, withinQuery: within, strategy: DefaultScrollStrategy, swipeSpeed: InstantSwipeSpeed, withInertia: false, timeout: timeOut ); }; } // First step. Ignore misses if search is two-step process switch (direction) { case FindScrollDirection.Down: executeScrollDownTo(); return(target); case FindScrollDirection.DownThenUp: try { executeScrollDownTo(); } catch (Exception) { } break; case FindScrollDirection.Up: executeScrollUpTo(); return(target); case FindScrollDirection.UpThenDown: try { executeScrollUpTo(); } catch (Exception) { } break; } // Second step (if the search is a two step process) switch (direction) { case FindScrollDirection.DownThenUp: executeScrollUpTo(); break; case FindScrollDirection.UpThenDown: executeScrollDownTo(); break; } return(target); }
// Async versions of the Uno.UITest.Helpers.Queries.QueryExtensions // Those are only mapping to the sync version but are needed to allow // multi-targeting with **Runtime UI tests** engine which has only async versions. public static async Task WaitForDependencyPropertyValueAsync( this IApp app, QueryEx element, string dependencyPropertyName, string value) => app.WaitForDependencyPropertyValue(element, dependencyPropertyName, value);
/// <summary> /// Set the current scroll context (used in operations such as <see cref="FindWithinContext"/>), and returns /// a disposable handle which will restore the previous context when disposed /// </summary> /// <param name="app">The application</param> /// <param name="scrollContext">The scroll context to set</param> /// <returns></returns> public static IDisposable WithScrollContext(IApp app, QueryEx scrollContext) { return(new ScrollContextTracker(scrollContext)); }
// [Timeout(3000000)] // Timeout is now moved to individual platorms runners configuration in CI public async Task RunRuntimeTests() { Run("SamplesApp.Samples.UnitTests.UnitTestsPage"); IAppQuery AllQuery(IAppQuery query) // .All() is not yet supported for wasm. => AppInitializer.GetLocalPlatform() == Platform.Browser ? query : query.All(); var runButton = new QueryEx(q => AllQuery(q).Marked("runButton")); var failedTests = new QueryEx(q => AllQuery(q).Marked("failedTests")); var failedTestsDetails = new QueryEx(q => AllQuery(q).Marked("failedTestDetails")); var unitTestsControl = new QueryEx(q => AllQuery(q).Marked("UnitTestsRootControl")); async Task <bool> IsTestExecutionDone() { return(await GetWithRetry("IsTestExecutionDone", () => unitTestsControl.GetDependencyPropertyValue("RunningStateForUITest")?.ToString().Equals("Finished", StringComparison.OrdinalIgnoreCase) ?? false)); } _app.WaitForElement(runButton); if (Environment.GetEnvironmentVariable(TestResultsOutputFilePath) is { } path) { // Used to disable showing the test output visually unitTestsControl.SetDependencyPropertyValue("IsRunningOnCI", "true"); // Used to perform test grouping on CI to reduce the impact of re-runs if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(TestGroupVariable))) { unitTestsControl.SetDependencyPropertyValue("CITestGroup", Environment.GetEnvironmentVariable(TestGroupVariable)); unitTestsControl.SetDependencyPropertyValue("CITestGroupCount", Environment.GetEnvironmentVariable(TestGroupCountVariable)); } } _app.FastTap(runButton); var lastChange = DateTimeOffset.Now; var lastValue = ""; while (DateTimeOffset.Now - lastChange < TestRunTimeout) { var newValue = await GetWithRetry("GetRunTestCount", () => unitTestsControl.GetDependencyPropertyValue("RunTestCountForUITest")?.ToString()); if (lastValue != newValue) { lastChange = DateTimeOffset.Now; } await Task.Delay(TimeSpan.FromSeconds(.5)); if (await IsTestExecutionDone()) { break; } } if (!await IsTestExecutionDone()) { Assert.Fail("A test run timed out"); } TestContext.AddTestAttachment(await ArchiveResults(unitTestsControl), "runtimetests-results.zip"); var count = GetValue(nameof(unitTestsControl), unitTestsControl, "FailedTestCountForUITest"); if (count != "0") { var tests = GetValue(nameof(failedTests), failedTests) .Split(new char[] { '§' }, StringSplitOptions.RemoveEmptyEntries) .Select((x, i) => $"\t{i + 1}. {x}\n") .ToArray(); var details = GetValue(nameof(failedTestsDetails), failedTestsDetails); Assert.Fail($"{tests.Length} unit test(s) failed (count={count}).\n\tFailing Tests:\n{string.Join("", tests)}\n\n---\n\tDetails:\n{details}"); } TakeScreenshot("Runtime Tests Results", ignoreInSnapshotCompare: true); }
private void TranslateOverElement(QueryEx element) { var rect = _app.WaitForElement(element).Single().Rect; _app.DragCoordinates(rect.X + 2, rect.Y + 2, rect.Right - 2, rect.Bottom - 2); }