Browse Source

Add tests for middle-click open-in-new-tab and Ctrl+R analyze

Two implemented tree gestures had no coverage (flagged by a gesture audit): middle mouse button on a row opens it in a new document tab, and Ctrl+R analyzes the selected member into the analyzer pane.

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3755/head
Siegfried Pammer 1 month ago
parent
commit
c52a3f92e2
  1. 33
      ILSpy.Tests/Analyzers/AnalyzerTreeKeyboardTests.cs
  2. 44
      ILSpy.Tests/ContextMenus/DecompileInNewViewTests.cs

33
ILSpy.Tests/Analyzers/AnalyzerTreeKeyboardTests.cs

@ -16,6 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
@ -68,4 +69,36 @@ public class AnalyzerTreeKeyboardTests
await Waiters.WaitForAsync(() => analyzed.IsExpanded, await Waiters.WaitForAsync(() => analyzed.IsExpanded,
description: "Right must expand the node via the shared TreeKeyboardController on the analyzer tree"); description: "Right must expand the node via the shared TreeKeyboardController on the analyzer tree");
} }
[AvaloniaTest]
public async Task Ctrl_R_Analyzes_The_Selected_Member()
{
// Ctrl+R on the assembly tree analyzes the selected member(s) -- the keyboard equivalent of the
// Analyze context-menu entry. Here a selected type lands as a node in the analyzer pane.
var (window, vm) = await TestHarness.BootAsync(3);
var analyzerVm = AppComposition.Current.GetExport<AnalyzerTreeViewModel>();
var pane = await window.WaitForComponent<global::ILSpy.AssemblyTree.AssemblyListPane>();
var tree = await pane.WaitForComponent<global::ILSpy.Controls.TreeView.SharpTreeView>();
var typeNode = vm.AssemblyTreeModel.FindNode<TypeTreeNode>(
"System.Linq", "System.Linq", "System.Linq.Enumerable");
vm.AssemblyTreeModel.SelectNode(typeNode);
for (int i = 0; i < 6; i++)
{
Dispatcher.UIThread.RunJobs();
tree.UpdateLayout();
await Task.Delay(20);
}
tree.Focus();
Dispatcher.UIThread.RunJobs();
int before = analyzerVm.Root.Children.Count;
window.KeyPress(Key.R, RawInputModifiers.Control, PhysicalKey.R, null);
await Waiters.WaitForAsync(() => analyzerVm.Root.Children.Count > before,
description: "Ctrl+R must add an analyzer node for the selected member");
analyzerVm.Root.Children.OfType<AnalyzerEntityTreeNode>()
.Any(n => n.Member is { } m && m.MetadataToken == ((ITypeDefinition)typeNode.Member!).MetadataToken)
.Should().BeTrue("the analyzer pane must hold a node for the type that Ctrl+R analyzed");
}
} }

44
ILSpy.Tests/ContextMenus/DecompileInNewViewTests.cs

@ -286,6 +286,50 @@ public class DecompileInNewViewTests
"B's highlight must be gone"); "B's highlight must be gone");
} }
[AvaloniaTest]
public async Task Middle_Clicking_A_Row_Opens_It_In_A_New_Tab()
{
// Middle mouse button on a tree row opens that node in a fresh document tab (mirrors the
// "Decompile to new tab" gesture without going through the context menu).
var window = AppComposition.Current.GetExport<MainWindow>();
window.Show();
var vm = (MainWindowViewModel)window.DataContext!;
await vm.AssemblyTreeModel.WaitForAssembliesAsync(minimumCount: 3);
var pane = await window.WaitForComponent<AssemblyListPane>();
var grid = await pane.WaitForComponent<global::ILSpy.Controls.TreeView.SharpTreeView>();
var nodeA = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>("System.Linq");
var nodeB = vm.AssemblyTreeModel.FindNode<AssemblyTreeNode>(TreeNavigation.CoreLibName);
vm.AssemblyTreeModel.SelectNode(nodeA);
for (int i = 0; i < 8; i++)
{
Dispatcher.UIThread.RunJobs();
grid.UpdateLayout();
await Task.Delay(25);
}
var rowB = grid.GetVisualDescendants().OfType<global::ILSpy.Controls.TreeView.SharpTreeViewItem>()
.First(r => RowNodeEquals(r, nodeB));
int tabsBefore = vm.DockWorkspace.Documents!.VisibleDockables!.OfType<ContentTabPage>().Count();
var clickX = System.Math.Min(rowB.Bounds.Width, grid.Bounds.Width) / 2;
var point = rowB.TranslatePoint(new Point(clickX, rowB.Bounds.Height / 2), window)!.Value;
HeadlessWindowExtensions.MouseDown(window, point, MouseButton.Middle);
HeadlessWindowExtensions.MouseUp(window, point, MouseButton.Middle);
for (int i = 0; i < 8; i++)
{
Dispatcher.UIThread.RunJobs();
grid.UpdateLayout();
await Task.Delay(20);
}
vm.DockWorkspace.Documents!.VisibleDockables!.OfType<ContentTabPage>().Count()
.Should().BeGreaterThan(tabsBefore, "middle-clicking a row must open it in a new document tab");
// Activating the new tab pulls the tree selection across to its node.
ReferenceEquals(vm.AssemblyTreeModel.SelectedItem, nodeB).Should().BeTrue(
"the new tab opened by middle-click must be sourced from the middle-clicked node");
}
[AvaloniaTest] [AvaloniaTest]
public async Task Opening_A_Node_In_A_New_Tab_Syncs_The_Tree_Selection_To_It() public async Task Opening_A_Node_In_A_New_Tab_Syncs_The_Tree_Selection_To_It()
{ {

Loading…
Cancel
Save