mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
5.3 KiB
154 lines
5.3 KiB
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using System.Linq; |
|
|
|
using Avalonia.Controls; |
|
using Avalonia.Headless; |
|
using Avalonia.Headless.NUnit; |
|
using Avalonia.Input; |
|
using Avalonia.Threading; |
|
|
|
using AwesomeAssertions; |
|
|
|
using ICSharpCode.ILSpyX.TreeView; |
|
|
|
using ICSharpCode.ILSpy.Controls.TreeView; |
|
|
|
using NUnit.Framework; |
|
|
|
namespace ICSharpCode.ILSpy.Tests.Controls; |
|
|
|
[TestFixture] |
|
public class SharpTreeViewTests |
|
{ |
|
sealed class TestNode : SharpTreeNode |
|
{ |
|
readonly string text; |
|
public TestNode(string text, params TestNode[] children) |
|
{ |
|
this.text = text; |
|
foreach (var child in children) |
|
Children.Add(child); |
|
} |
|
public override object Text => text; |
|
} |
|
|
|
static (Window window, SharpTreeView tree, TestNode root) Host() |
|
{ |
|
var b1 = new TestNode("B1"); |
|
var b = new TestNode("B", b1); |
|
var root = new TestNode("root", new TestNode("A"), b, new TestNode("C")); |
|
var tree = new SharpTreeView { ShowRoot = false, Root = root }; |
|
var window = new Window { Content = tree, Width = 300, Height = 400 }; |
|
window.Show(); |
|
Dispatcher.UIThread.RunJobs(); |
|
return (window, tree, root); |
|
} |
|
|
|
[AvaloniaTest] |
|
public void Flattens_Visible_Nodes_And_Expand_Collapse_Updates_Row_Count() |
|
{ |
|
var (_, tree, root) = Host(); |
|
var b = (TestNode)root.Children[1]; |
|
|
|
tree.ItemCount.Should().Be(3, "A, B (collapsed), C are the visible top-level rows"); |
|
|
|
b.IsExpanded = true; |
|
Dispatcher.UIThread.RunJobs(); |
|
tree.ItemCount.Should().Be(4, "expanding B reveals B1"); |
|
|
|
b.IsExpanded = false; |
|
Dispatcher.UIThread.RunJobs(); |
|
tree.ItemCount.Should().Be(3, "collapsing B hides B1 again"); |
|
} |
|
|
|
[AvaloniaTest] |
|
public void Shift_Down_Extends_And_Shift_Up_Shrinks_From_The_Anchor() |
|
{ |
|
// The whole reason for the rewrite: native ListBox anchor selection both extends AND |
|
// shrinks a shift-range -- the thing ProDataGrid's additive SetRowsSelection could not do. |
|
var nodes = Enumerable.Range(0, 5).Select(i => new TestNode(((char)('A' + i)).ToString())).ToArray(); |
|
var root = new TestNode("root", nodes); |
|
var tree = new SharpTreeView { ShowRoot = false, Root = root }; |
|
var window = new Window { Content = tree, Width = 300, Height = 400 }; |
|
window.Show(); |
|
Dispatcher.UIThread.RunJobs(); |
|
|
|
tree.SelectedItem = nodes[0]; |
|
Dispatcher.UIThread.RunJobs(); |
|
(tree.ContainerFromItem(nodes[0]))?.Focus(); |
|
Dispatcher.UIThread.RunJobs(); |
|
|
|
window.KeyPress(Key.Down, RawInputModifiers.Shift, PhysicalKey.ArrowDown, null); |
|
Dispatcher.UIThread.RunJobs(); |
|
window.KeyPress(Key.Down, RawInputModifiers.Shift, PhysicalKey.ArrowDown, null); |
|
Dispatcher.UIThread.RunJobs(); |
|
tree.SelectedItems!.Cast<SharpTreeNode>().Should() |
|
.BeEquivalentTo(new[] { nodes[0], nodes[1], nodes[2] }, "Shift+Down twice extends A..C"); |
|
|
|
window.KeyPress(Key.Up, RawInputModifiers.Shift, PhysicalKey.ArrowUp, null); |
|
Dispatcher.UIThread.RunJobs(); |
|
tree.SelectedItems!.Cast<SharpTreeNode>().Should() |
|
.BeEquivalentTo(new[] { nodes[0], nodes[1] }, "Shift+Up shrinks the range back toward the anchor (A,B)"); |
|
} |
|
|
|
[AvaloniaTest] |
|
public void Type_Ahead_Jumps_To_The_Visible_Node_Matching_The_Typed_Prefix() |
|
{ |
|
var apple = new TestNode("Apple"); |
|
var banana = new TestNode("Banana"); |
|
var cherry = new TestNode("Cherry"); |
|
var cranberry = new TestNode("Cranberry"); |
|
var root = new TestNode("root", apple, banana, cherry, cranberry); |
|
var tree = new SharpTreeView { ShowRoot = false, Root = root }; |
|
var window = new Window { Content = tree, Width = 300, Height = 400 }; |
|
window.Show(); |
|
Dispatcher.UIThread.RunJobs(); |
|
|
|
// "cra": 'c' lands on Cherry, 'r' has to search forward to Cranberry, 'a' stays. |
|
foreach (char ch in "cra") |
|
{ |
|
tree.RaiseEvent(new TextInputEventArgs { |
|
RoutedEvent = InputElement.TextInputEvent, |
|
Text = ch.ToString(), |
|
Source = tree, |
|
}); |
|
Dispatcher.UIThread.RunJobs(); |
|
} |
|
|
|
tree.SelectedItem.Should().BeSameAs(cranberry, "type-ahead refines forward to the Cr* match"); |
|
} |
|
|
|
[AvaloniaTest] |
|
public void Selecting_A_Row_Sets_The_Node_IsSelected() |
|
{ |
|
var (_, tree, root) = Host(); |
|
var a = (TestNode)root.Children[0]; |
|
var c = (TestNode)root.Children[2]; |
|
|
|
tree.SelectedItem = a; |
|
Dispatcher.UIThread.RunJobs(); |
|
a.IsSelected.Should().BeTrue(); |
|
|
|
tree.SelectedItem = c; |
|
Dispatcher.UIThread.RunJobs(); |
|
c.IsSelected.Should().BeTrue(); |
|
a.IsSelected.Should().BeFalse("moving the selection clears the old node's flag"); |
|
} |
|
}
|
|
|