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 1 commit
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
9 changes: 7 additions & 2 deletions src/TestCentric/testcentric.gui/Presenters/DisplayStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,19 @@ public string GroupDisplayName(TestGroup group)
return string.Format("{0} ({1})", group.Name, group.Count());
}

protected virtual string GetTestNodeName(TestNode testNode)
{
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 GetTestNodeDisplayName(TestNode testNode)
{
string treeNodeName = testNode.Name;
string treeNodeName = GetTestNodeName(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
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,100 @@ 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 GetTestNodeName(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.GetTestNodeName(testNode);
}

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

if (ShowTreeNodeType(testNode))
{
if (IsNamespaceNode(testNode))
testNode = GetFoldedNamespaceNode(testNode);

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 TestNode GetFoldedNamespaceNode(TestNode testNode)
{
// Get list of all namespace nodes which can be folded
IList<TestNode> foldedNodes = FoldNamespaceNodes(testNode);

// Get name of folded namespaces and store in dictionary for later usage
TestNode resultNode = foldedNodes.Last();
_foldedNodeNames[resultNode] = GetFoldedNamespaceName(foldedNodes);
return resultNode;
}

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.Checked = _settings.Gui.TestTree.ShowNamespace;
break;
}
};

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

_view.ShowNamespace.CheckedChanged += () =>
{
_settings.Gui.TestTree.ShowNamespace = _view.ShowNamespace.Checked;
};

_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.Checked = _settings.Gui.TestTree.ShowNamespace;
_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; }
IChecked ShowNamespace { get; }
ICommand RunParametersButton { get; }

IChecked RunSummaryButton { get; }
Expand Down
15 changes: 15 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,7 @@ public class TestCentricMainView : TestCentricFormBase, IMainView
private ToolStripMenuItem nunitTreeMenuItem;
private ToolStripMenuItem fixtureListMenuItem;
private ToolStripMenuItem testListMenuItem;
private ToolStripMenuItem nunitTreeShowNamespaceMenuItem;
private ToolStripSeparator toolStripSeparator13;
private ToolStripMenuItem byAssemblyMenuItem;
private ToolStripMenuItem byFixtureMenuItem;
Expand Down Expand Up @@ -175,6 +176,7 @@ public TestCentricMainView() : base("TestCentric")
GroupBy = new CheckedToolStripMenuGroup(
"testGrouping",
byAssemblyMenuItem, byFixtureMenuItem, byCategoryMenuItem, byOutcomeMenuItem, byDurationMenuItem);
ShowNamespace = new CheckedMenuElement(nunitTreeShowNamespaceMenuItem);
RunParametersButton = new ToolStripButtonElement(runParametersButton);
RunSummaryButton = new CheckBoxElement(runSummaryButton);

Expand Down Expand Up @@ -217,6 +219,7 @@ 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.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 +408,24 @@ 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 = "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 });

//
// fixtureListMenuItem
//
Expand Down Expand Up @@ -1124,6 +1138,7 @@ public int SplitterPosition
public IViewElement DisplayFormatButton { get; private set; }
public ISelection DisplayFormat { get; private set; }
public ISelection GroupBy { get; private set; }
public IChecked 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(true)]
[TestCase(false)]
public void ShowNamespaceChanged_ChangesModelSetting(bool isChecked)
{
// Arrange
_view.ShowNamespace.Checked.Returns(isChecked);

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

// Assert
Assert.That(_model.Settings.Gui.TestTree.ShowNamespace, Is.EqualTo(isChecked));
}
}
}
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)]
[TestCase(false)]
public void ShowNamespace_SettingChanged_MenuItemIsUpdated(bool showNamespace)
{
// 1. Act
_settings.Gui.TestTree.ShowNamespace = showNamespace;

// 2. Assert
_view.ShowNamespace.Checked = showNamespace;
}
}
}
Loading