Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NUnit tree: add option to show/hide namespace nodes; add folding of namespace nodes #1153

Merged
merged 2 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/TestCentric/testcentric.gui/Presenters/DisplayStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ protected TreeNode MakeTreeNode(TestGroup group, bool recursive)

public TreeNode MakeTreeNode(TestNode testNode, bool recursive)
{
string treeNodeName = GetTestNodeDisplayName(testNode);
string treeNodeName = GetTreeNodeDisplayName(testNode);
TreeNode treeNode = new TreeNode(treeNodeName);
treeNode.Tag = testNode;

Expand Down Expand Up @@ -176,14 +176,19 @@ public string GroupDisplayName(TestGroup group)
return string.Format("{0} ({1})", group.Name, group.Count());
}

private string GetTestNodeDisplayName(TestNode testNode)
protected virtual string GetTreeNodeName(TestNode testNode)
{
string treeNodeName = testNode.Name;
return testNode.Name;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this method is slightly confusing. Up till now, each tree node representeed only one test node. But now, with folding, it can map to multiple namespaces. So... each test node has a name and the tree node may have a combined name. It would be great if this method and the next one could reflect that in some way. GetTreeNodeName and GetTreeNodeDisplayName?

private string GetTreeNodeDisplayName(TestNode testNode)
{
string treeNodeName = GetTreeNodeName(testNode);

// Check if test result is available for this node
ResultNode result = _model.GetResultForTest(testNode.Id);
if (_settings.Gui.TestTree.ShowTestDuration && result != null)
treeNodeName = testNode.Name + $" [{result.Duration:0.000}s]";
treeNodeName += $" [{result.Duration:0.000}s]";

return treeNodeName;
}
Expand Down Expand Up @@ -212,7 +217,7 @@ private void UpdateTreeNodeName(TreeNode treeNode)
if (testNode == null)
return;

string treeNodeName = GetTestNodeDisplayName(testNode);
string treeNodeName = GetTreeNodeDisplayName(testNode);
_view.InvokeIfRequired(() => treeNode.Text = treeNodeName);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

namespace TestCentric.Gui.Presenters
{
using System.IO;
using System.Collections.Generic;
using System.Linq;
using Model;
using Views;

Expand All @@ -18,6 +19,8 @@ namespace TestCentric.Gui.Presenters
/// </summary>
public class NUnitTreeDisplayStrategy : DisplayStrategy
{
private IDictionary<TestNode, string> _foldedNodeNames = new Dictionary<TestNode, string>();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you examine whether the base class dictionary, _nodeIndex, which is used for other purposes, could be expanded to serve the purpose of _foldedNodeNames? If not, we could look at it at a later time to decide whether it would simplify things.

public NUnitTreeDisplayStrategy(ITestTreeView view, ITestModel model)
: base(view, model) { }

Expand All @@ -32,17 +35,98 @@ public override string Description
public override void OnTestLoaded(TestNode testNode, VisualState visualState)
{
ClearTree();
_foldedNodeNames.Clear();

foreach (var topLevelNode in testNode.Children)
_view.Add(MakeTreeNode(topLevelNode, true));
_view.Add(CreateNUnitTreeNode(null, topLevelNode));

if (visualState != null)
visualState.ApplyTo(_view.TreeView);
else
SetDefaultInitialExpansion();
}

protected override VisualState CreateVisualState() => new VisualState("NUNIT_TREE").LoadFrom(_view.TreeView);
protected override VisualState CreateVisualState() => new VisualState("NUNIT_TREE", _settings.Gui.TestTree.ShowNamespace).LoadFrom(_view.TreeView);

protected override string GetTreeNodeName(TestNode testNode)
{
// For folded namespace nodes use the combined name of all folded nodes ("Library.Test.Folder")
if (_foldedNodeNames.TryGetValue(testNode, out var name))
return name;

return base.GetTreeNodeName(testNode);
}

private TreeNode CreateNUnitTreeNode(TreeNode parentNode, TestNode testNode)
{
TreeNode treeNode = null;

if (ShowTreeNodeType(testNode))
{
if (IsNamespaceNode(testNode))
{
// Get list of all namespace nodes which can be folded
// And get name of folded namespaces and store in dictionary for later usage
IList<TestNode> foldedNodes = FoldNamespaceNodes(testNode);
_foldedNodeNames[foldedNodes.First()] = GetFoldedNamespaceName(foldedNodes);

treeNode = MakeTreeNode(foldedNodes.First(), false); // Create TreeNode representing the first node
testNode = foldedNodes.Last(); // But proceed building up tree with last node
}
else
treeNode = MakeTreeNode(testNode, false);

parentNode?.Nodes.Add(treeNode);
parentNode = treeNode;
}

foreach (TestNode child in testNode.Children)
{
CreateNUnitTreeNode(parentNode, child);
}

return treeNode;
}

/// <summary>
/// Check if a tree node type should be shown or omitted
/// Currently we support only omitting the namespace nodes
/// </summary>
private bool ShowTreeNodeType(TestNode testNode)
{
if (IsNamespaceNode(testNode))
return _settings.Gui.TestTree.ShowNamespace;

return true;
}

private string GetFoldedNamespaceName(IList<TestNode> foldedNamespaces)
{
var namespaceNames = foldedNamespaces.Select(x => x.Name);
return String.Join(".", namespaceNames);
}

private IList<TestNode> FoldNamespaceNodes(TestNode testNode)
{
if (!IsNamespaceNode(testNode))
{
return new List<TestNode>();
}

// If a namespace node only contains one child item which is also a namespace node, we can fold them.
List<TestNode> namespaceNodes = new List<TestNode>() { testNode };
if (testNode.Children.Count == 1 && IsNamespaceNode(testNode.Children[0]))
{
namespaceNodes.AddRange(FoldNamespaceNodes(testNode.Children[0]));
}

return namespaceNodes;
}

private bool IsNamespaceNode(TestNode testNode)
{
return testNode.IsSuite && (testNode.Type == "TestSuite" || testNode.Type == "SetUpFixture");
}

private void SetDefaultInitialExpansion()
{
Expand Down
11 changes: 11 additions & 0 deletions src/TestCentric/testcentric.gui/Presenters/TestCentricPresenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ void OnRunFinished(ResultNode result)
case "TestCentric.Gui.TestTree.FixtureList.GroupBy":
_view.GroupBy.SelectedItem = _settings.Gui.TestTree.FixtureList.GroupBy;
break;
case "TestCentric.Gui.TestTree.ShowNamespace":
_view.ShowNamespace.SelectedIndex = _settings.Gui.TestTree.ShowNamespace ? 0 : 1;
break;
}
};

Expand Down Expand Up @@ -489,6 +492,11 @@ void OnRunFinished(ResultNode result)
_settings.Gui.TestTree.DisplayFormat = _view.DisplayFormat.SelectedItem;
};

_view.ShowNamespace.SelectionChanged += () =>
{
_settings.Gui.TestTree.ShowNamespace = _view.ShowNamespace.SelectedIndex == 0;
};

_view.GroupBy.SelectionChanged += () =>
{
switch(_view.DisplayFormat.SelectedItem)
Expand Down Expand Up @@ -945,6 +953,9 @@ private void UpdateTreeDisplayMenuItem()
_view.GroupBy.SelectedItem = _settings.Gui.TestTree.FixtureList.GroupBy;
break;
}

_view.ShowNamespace.SelectedIndex = _settings.Gui.TestTree.ShowNamespace ? 0 : 1;
_view.ShowNamespace.Enabled = displayFormat == "NUNIT_TREE";
}

private void RunAllTests()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ private void WireUpEvents()
}
case "TestCentric.Gui.TestTree.TestList.GroupBy":
case "TestCentric.Gui.TestTree.FixtureList.GroupBy":
case "TestCentric.Gui.TestTree.ShowNamespace":
Strategy?.Reload();
break;
case "TestCentric.Gui.TestTree.ShowCheckBoxes":
Expand Down Expand Up @@ -283,6 +284,8 @@ private void UpdateTreeSettingsFromVisualState(VisualState visualState)
{
_treeSettings.FixtureList.GroupBy = visualState.GroupBy;
}

_treeSettings.ShowNamespace = visualState.ShowNamespace;
}

private void EnsureNonRunnableFilesAreVisible(TestNode testNode)
Expand Down
1 change: 1 addition & 0 deletions src/TestCentric/testcentric.gui/Views/IMainView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public interface IMainView
IViewElement DisplayFormatButton { get; }
ISelection DisplayFormat { get; }
ISelection GroupBy { get; }
ISelection ShowNamespace { get; }
ICommand RunParametersButton { get; }

IChecked RunSummaryButton { get; }
Expand Down
25 changes: 25 additions & 0 deletions src/TestCentric/testcentric.gui/Views/TestCentricMainView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public class TestCentricMainView : TestCentricFormBase, IMainView
private ToolStripMenuItem nunitTreeMenuItem;
private ToolStripMenuItem fixtureListMenuItem;
private ToolStripMenuItem testListMenuItem;
private ToolStripMenuItem nunitTreeShowNamespaceMenuItem;
private ToolStripMenuItem nunitTreeHideNamespaceMenuItem;
private ToolStripSeparator toolStripSeparator13;
private ToolStripMenuItem byAssemblyMenuItem;
private ToolStripMenuItem byFixtureMenuItem;
Expand Down Expand Up @@ -175,6 +177,7 @@ public TestCentricMainView() : base("TestCentric")
GroupBy = new CheckedToolStripMenuGroup(
"testGrouping",
byAssemblyMenuItem, byFixtureMenuItem, byCategoryMenuItem, byOutcomeMenuItem, byDurationMenuItem);
ShowNamespace = new CheckedToolStripMenuGroup("showNamespace", nunitTreeShowNamespaceMenuItem, nunitTreeHideNamespaceMenuItem);
RunParametersButton = new ToolStripButtonElement(runParametersButton);
RunSummaryButton = new CheckBoxElement(runSummaryButton);

Expand Down Expand Up @@ -217,6 +220,8 @@ private void InitializeComponent()
this.nunitTreeMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.fixtureListMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.testListMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.nunitTreeShowNamespaceMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.nunitTreeHideNamespaceMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator13 = new System.Windows.Forms.ToolStripSeparator();
this.byAssemblyMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.byFixtureMenuItem = new System.Windows.Forms.ToolStripMenuItem();
Expand Down Expand Up @@ -405,13 +410,32 @@ private void InitializeComponent()
this.displayFormatButton.Size = new System.Drawing.Size(29, 21);
this.displayFormatButton.Text = "Display";
this.displayFormatButton.ToolTipText = "Tree Display Format";

//
// nunitTreeShowNamespaceMenuItem
//
this.nunitTreeShowNamespaceMenuItem.Name = "NUNIT_TREE_SHOW_NAMESPACE";
this.nunitTreeShowNamespaceMenuItem.Size = new System.Drawing.Size(198, 22);
this.nunitTreeShowNamespaceMenuItem.Tag = "NUNIT_TREE_SHOW_NAMESPACE";
this.nunitTreeShowNamespaceMenuItem.Text = "Show Namespace";

//
// nunitTreeHideNamespaceMenuItem
//
this.nunitTreeHideNamespaceMenuItem.Name = "NUNIT_TREE_HIDE_NAMESPACE";
this.nunitTreeHideNamespaceMenuItem.Size = new System.Drawing.Size(198, 22);
this.nunitTreeHideNamespaceMenuItem.Tag = "NUNIT_TREE_HIDE_NAMESPACE";
this.nunitTreeHideNamespaceMenuItem.Text = "Hide Namespace";

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make the text "Show Namespace" so it's clear what it does. I'd actually prefer to see both Show Namespace and Hide Namespace exclusive radio buttons to make it completely clear, but I think we will redo this whole menu thing at least one more time so it could wait.

//
// nunitTreeMenuItem
//
this.nunitTreeMenuItem.Name = "nunitTreeMenuItem";
this.nunitTreeMenuItem.Size = new System.Drawing.Size(198, 22);
this.nunitTreeMenuItem.Tag = "NUNIT_TREE";
this.nunitTreeMenuItem.Text = "NUnit Tree";
nunitTreeMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { nunitTreeShowNamespaceMenuItem, nunitTreeHideNamespaceMenuItem });

//
// fixtureListMenuItem
//
Expand Down Expand Up @@ -1124,6 +1148,7 @@ public int SplitterPosition
public IViewElement DisplayFormatButton { get; private set; }
public ISelection DisplayFormat { get; private set; }
public ISelection GroupBy { get; private set; }
public ISelection ShowNamespace { get; private set; }
public ICommand RunParametersButton { get; private set; }

public IChecked RunSummaryButton { get; private set; }
Expand Down
13 changes: 12 additions & 1 deletion src/TestCentric/testcentric.gui/VisualState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ public VisualState(string strategyID)
DisplayStrategy = strategyID;
}

public VisualState(string strategyID, bool showNamespace)
{
DisplayStrategy = strategyID;
ShowNamespace = showNamespace;
}

public VisualState(string strategyID, string groupID)
{
if (strategyID == "NUNIT_TREE")
Expand All @@ -53,6 +59,8 @@ public VisualState(string strategyID, string groupID)
//[XmlAttribute, DefaultValue(false)]
public bool ShowCheckBoxes;

public bool ShowNamespace;

// TODO: Categories not yet supported
//public List<string> SelectedCategories;
//public bool ExcludeCategories;
Expand Down Expand Up @@ -250,6 +258,7 @@ public void ReadXml(XmlReader reader)
// GroupBy is null for NUnitTree strategy, otherwise required
if (GroupBy == null && strategy != "NUNIT_TREE") GroupBy = "ASSEMBLY";
ShowCheckBoxes = reader.GetAttribute("ShowCheckBoxes") == "True";
ShowNamespace = reader.GetAttribute("ShowNamespace") == "True";

while (reader.Read())
{
Expand Down Expand Up @@ -364,7 +373,9 @@ public void WriteXml(XmlWriter writer)
writer.WriteAttributeString("GroupBy", GroupBy);
if (ShowCheckBoxes)
writer.WriteAttributeString("ShowCheckBoxes", "True");

if (ShowNamespace)
writer.WriteAttributeString("ShowNamespace", "True");

WriteVisualTreeNodes(Nodes);

void WriteVisualTreeNodes(List<VisualTreeNode> nodes)
Expand Down
14 changes: 14 additions & 0 deletions src/TestCentric/tests/Presenters/Main/CommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -349,5 +349,19 @@ public void SelectedTestsChanged_TestSelected_CommandIsEnabled()
// Assert
Assert.That(_view.RunSelectedButton.Enabled, Is.True);
}

[TestCase(0, true)]
[TestCase(1, false)]
public void ShowNamespaceChanged_ChangesModelSetting(int selectedMenuItem, bool expectedShowNamespace)
{
// Arrange
_view.ShowNamespace.SelectedIndex.Returns(selectedMenuItem);

// Act
_view.ShowNamespace.SelectionChanged += Raise.Event<CommandHandler>();

// Assert
Assert.That(_model.Settings.Gui.TestTree.ShowNamespace, Is.EqualTo(expectedShowNamespace));
}
}
}
11 changes: 11 additions & 0 deletions src/TestCentric/tests/Presenters/Main/WhenSettingsChanged.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,16 @@ public void TestListGroupBy_SettingChanged_MenuItemIsUpdated(string groupBy)
// 2. Assert
_view.GroupBy.SelectedItem = groupBy;
}

[TestCase(true, 0)]
[TestCase(false, 1)]
public void ShowNamespace_SettingChanged_MenuItemIsUpdated(bool showNamespace, int expectedMenuIndex)
{
// 1. Act
_settings.Gui.TestTree.ShowNamespace = showNamespace;

// 2. Assert
_view.ShowNamespace.SelectedIndex = expectedMenuIndex;
}
}
}
Loading