Ionite
1 year ago
9 changed files with 220 additions and 10 deletions
@ -0,0 +1,12 @@ |
|||||||
|
namespace StabilityMatrix.UITests.Attributes; |
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Method)] |
||||||
|
public class TestPriorityAttribute : Attribute |
||||||
|
{ |
||||||
|
public int Priority { get; private set; } |
||||||
|
|
||||||
|
public TestPriorityAttribute(int priority) |
||||||
|
{ |
||||||
|
Priority = priority; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
using Avalonia.Controls; |
||||||
|
|
||||||
|
namespace StabilityMatrix.UITests.Extensions; |
||||||
|
|
||||||
|
public static class VisualExtensions |
||||||
|
{ |
||||||
|
public static Rect GetRelativeBounds(this Visual visual, TopLevel topLevel) |
||||||
|
{ |
||||||
|
var origin = |
||||||
|
visual.TranslatePoint(new Point(0, 0), topLevel) |
||||||
|
?? throw new NullReferenceException("Origin is null"); |
||||||
|
|
||||||
|
var bounds = new Rect(origin, visual.Bounds.Size); |
||||||
|
|
||||||
|
return bounds; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
using Avalonia.Controls; |
||||||
|
using Avalonia.Threading; |
||||||
|
using Avalonia.VisualTree; |
||||||
|
|
||||||
|
namespace StabilityMatrix.UITests.Extensions; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Window extensions for UI tests |
||||||
|
/// </summary> |
||||||
|
public static class WindowExtensions |
||||||
|
{ |
||||||
|
public static void ClickTarget(this TopLevel topLevel, Control target) |
||||||
|
{ |
||||||
|
// Check target is part of the visual tree |
||||||
|
var targetVisualRoot = target.GetVisualRoot(); |
||||||
|
if (targetVisualRoot is not TopLevel) |
||||||
|
{ |
||||||
|
throw new ArgumentException("Target is not part of the visual tree"); |
||||||
|
} |
||||||
|
if (targetVisualRoot.Equals(topLevel)) |
||||||
|
{ |
||||||
|
throw new ArgumentException( |
||||||
|
"Target is not part of the same visual tree as the top level" |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
var point = |
||||||
|
target.TranslatePoint( |
||||||
|
new Point(target.Bounds.Width / 2, target.Bounds.Height / 2), |
||||||
|
topLevel |
||||||
|
) ?? throw new NullReferenceException("Point is null"); |
||||||
|
|
||||||
|
topLevel.MouseMove(point); |
||||||
|
topLevel.MouseDown(point, MouseButton.Left); |
||||||
|
topLevel.MouseUp(point, MouseButton.Left); |
||||||
|
|
||||||
|
// Return mouse to outside of window |
||||||
|
topLevel.MouseMove(new Point(-50, -50)); |
||||||
|
} |
||||||
|
|
||||||
|
public static async Task ClickTargetAsync(this TopLevel topLevel, Control target) |
||||||
|
{ |
||||||
|
// Check target is part of the visual tree |
||||||
|
var targetVisualRoot = target.GetVisualRoot(); |
||||||
|
if (targetVisualRoot is not TopLevel) |
||||||
|
{ |
||||||
|
throw new ArgumentException("Target is not part of the visual tree"); |
||||||
|
} |
||||||
|
if (!targetVisualRoot.Equals(topLevel)) |
||||||
|
{ |
||||||
|
throw new ArgumentException( |
||||||
|
"Target is not part of the same visual tree as the top level" |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
var point = |
||||||
|
target.TranslatePoint( |
||||||
|
new Point(target.Bounds.Width / 2, target.Bounds.Height / 2), |
||||||
|
topLevel |
||||||
|
) ?? throw new NullReferenceException("Point is null"); |
||||||
|
|
||||||
|
topLevel.MouseMove(point); |
||||||
|
topLevel.MouseDown(point, MouseButton.Left); |
||||||
|
topLevel.MouseUp(point, MouseButton.Left); |
||||||
|
|
||||||
|
await Task.Delay(40); |
||||||
|
|
||||||
|
// Return mouse to outside of window |
||||||
|
topLevel.MouseMove(new Point(-50, -50)); |
||||||
|
|
||||||
|
Dispatcher.UIThread.Invoke(() => Dispatcher.UIThread.RunJobs()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
using StabilityMatrix.UITests.Attributes; |
||||||
|
using Xunit.Abstractions; |
||||||
|
using Xunit.Sdk; |
||||||
|
|
||||||
|
namespace StabilityMatrix.UITests; |
||||||
|
|
||||||
|
public class PriorityOrderer : ITestCaseOrderer |
||||||
|
{ |
||||||
|
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases) |
||||||
|
where TTestCase : ITestCase |
||||||
|
{ |
||||||
|
var sortedMethods = new SortedDictionary<int, List<TTestCase>>(); |
||||||
|
|
||||||
|
foreach (var testCase in testCases) |
||||||
|
{ |
||||||
|
var priority = 0; |
||||||
|
|
||||||
|
foreach ( |
||||||
|
var attr in testCase.TestMethod.Method.GetCustomAttributes( |
||||||
|
typeof(TestPriorityAttribute).AssemblyQualifiedName |
||||||
|
) |
||||||
|
) |
||||||
|
{ |
||||||
|
priority = attr.GetNamedArgument<int>("Priority"); |
||||||
|
} |
||||||
|
|
||||||
|
GetOrCreate(sortedMethods, priority).Add(testCase); |
||||||
|
} |
||||||
|
|
||||||
|
foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority])) |
||||||
|
{ |
||||||
|
list.Sort( |
||||||
|
(x, y) => |
||||||
|
StringComparer.OrdinalIgnoreCase.Compare( |
||||||
|
x.TestMethod.Method.Name, |
||||||
|
y.TestMethod.Method.Name |
||||||
|
) |
||||||
|
); |
||||||
|
foreach (var testCase in list) |
||||||
|
{ |
||||||
|
yield return testCase; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static TValue GetOrCreate<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key) |
||||||
|
where TValue : new() |
||||||
|
{ |
||||||
|
if (dictionary.TryGetValue(key, out var result)) |
||||||
|
return result; |
||||||
|
|
||||||
|
result = new TValue(); |
||||||
|
dictionary[key] = result; |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
Before Width: | Height: | Size: 196 KiB After Width: | Height: | Size: 202 KiB |
Loading…
Reference in new issue