Skip to content

Commit d297bfa

Browse files
committed
Add context menu items to run selected test or all tests in active module. Closes #4776
1 parent a56e3c7 commit d297bfa

File tree

10 files changed

+210
-2
lines changed

10 files changed

+210
-2
lines changed

Rubberduck.Core/UI/Command/MenuItems/ParentMenus/CodePaneContextParentMenu.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,7 @@ public enum CodePaneContextMenuItemDisplayOrder
2020
FindSymbol,
2121
FindAllReferences,
2222
FindAllImplementations,
23+
RunSelectedTestModule,
24+
RunSelectedTest
2325
}
2426
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Rubberduck.Parsing.VBA;
2+
using Rubberduck.UI.Command.MenuItems.ParentMenus;
3+
4+
namespace Rubberduck.UI.Command.MenuItems
5+
{
6+
public class RunSelectedTestMethodCommandMenuItem : CommandMenuItemBase
7+
{
8+
public RunSelectedTestMethodCommandMenuItem(RunSelectedTestMethodCommand command) : base(command) { }
9+
10+
public override string Key => "ContextMenu_RunSelectedTest";
11+
public override int DisplayOrder => (int)CodePaneContextMenuItemDisplayOrder.RunSelectedTest;
12+
13+
public override bool EvaluateCanExecute(RubberduckParserState state)
14+
{
15+
return state != null && Command.CanExecute(null);
16+
}
17+
18+
public override bool HiddenWhenDisabled => true;
19+
20+
public override bool BeginGroup => false;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Rubberduck.Parsing.VBA;
2+
using Rubberduck.UI.Command.MenuItems.ParentMenus;
3+
4+
namespace Rubberduck.UI.Command.MenuItems
5+
{
6+
public class RunSelectedTestModuleCommandMenuItem : CommandMenuItemBase
7+
{
8+
public RunSelectedTestModuleCommandMenuItem(RunSelectedTestModuleCommand command) : base(command) { }
9+
10+
public override string Key => "ContextMenu_RunSelectedTestModule";
11+
public override int DisplayOrder => (int)CodePaneContextMenuItemDisplayOrder.RunSelectedTestModule;
12+
13+
public override bool EvaluateCanExecute(RubberduckParserState state)
14+
{
15+
return state != null && Command.CanExecute(null);
16+
}
17+
18+
public override bool HiddenWhenDisabled => true;
19+
20+
public override bool BeginGroup => true;
21+
}
22+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System.Linq;
2+
using NLog;
3+
using Rubberduck.Parsing.Annotations;
4+
using Rubberduck.Parsing.Symbols;
5+
using Rubberduck.Parsing.VBA;
6+
using Rubberduck.UnitTesting;
7+
using Rubberduck.VBEditor.Utility;
8+
9+
namespace Rubberduck.UI.Command
10+
{
11+
public class RunSelectedTestMethodCommand : CommandBase
12+
{
13+
private readonly ITestEngine _engine;
14+
private readonly ISelectionService _selectionService;
15+
private readonly IDeclarationFinderProvider _finderProvider;
16+
17+
public RunSelectedTestMethodCommand(ITestEngine engine, ISelectionService selectionService, IDeclarationFinderProvider finderProvider)
18+
: base(LogManager.GetCurrentClassLogger())
19+
{
20+
_engine = engine;
21+
_selectionService = selectionService;
22+
_finderProvider = finderProvider;
23+
}
24+
25+
protected override bool EvaluateCanExecute(object parameter)
26+
{
27+
return (parameter ?? FindDeclarationFromSelection()) is Declaration candidate &&
28+
!(_engine.Tests.FirstOrDefault(test => test.Declaration.Equals(candidate)) is null) &&
29+
_engine.CanRun;
30+
}
31+
32+
protected override void OnExecute(object parameter)
33+
{
34+
if (!((parameter ?? FindDeclarationFromSelection()) is Declaration candidate) ||
35+
!(_engine.Tests.FirstOrDefault(test => test.Declaration.Equals(candidate)) is TestMethod selectedTest) ||
36+
!_engine.CanRun)
37+
{
38+
return;
39+
}
40+
41+
_engine.Run(new [] { selectedTest });
42+
}
43+
44+
private Declaration FindDeclarationFromSelection()
45+
{
46+
var active = _selectionService?.ActiveSelection();
47+
if (!active.HasValue)
48+
{
49+
return null;
50+
}
51+
52+
return _finderProvider.DeclarationFinder.FindDeclarationsForSelection(active.Value)
53+
.SingleOrDefault(declaration => declaration.DeclarationType == DeclarationType.Procedure &&
54+
declaration.Annotations.Any(annotation => annotation is TestMethodAnnotation));
55+
}
56+
}
57+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using System.Linq;
2+
using NLog;
3+
using Rubberduck.Parsing.Annotations;
4+
using Rubberduck.Parsing.Symbols;
5+
using Rubberduck.Parsing.VBA;
6+
using Rubberduck.UnitTesting;
7+
using Rubberduck.VBEditor.Utility;
8+
9+
namespace Rubberduck.UI.Command
10+
{
11+
public class RunSelectedTestModuleCommand : CommandBase
12+
{
13+
private readonly ITestEngine _engine;
14+
private readonly ISelectionService _selectionService;
15+
private readonly IDeclarationFinderProvider _finderProvider;
16+
17+
public RunSelectedTestModuleCommand(ITestEngine engine, ISelectionService selectionService, IDeclarationFinderProvider finderProvider)
18+
: base(LogManager.GetCurrentClassLogger())
19+
{
20+
_engine = engine;
21+
_selectionService = selectionService;
22+
_finderProvider = finderProvider;
23+
}
24+
25+
protected override bool EvaluateCanExecute(object parameter)
26+
{
27+
return (parameter ?? FindModuleFromSelection()) is Declaration candidate &&
28+
candidate.DeclarationType == DeclarationType.ProceduralModule &&
29+
candidate.Annotations.Any(annotation => annotation is TestModuleAnnotation) &&
30+
_engine.CanRun &&
31+
_engine.Tests.Any(test => test.Declaration.QualifiedModuleName.Equals(candidate.QualifiedModuleName));
32+
}
33+
34+
protected override void OnExecute(object parameter)
35+
{
36+
if (!((parameter ?? FindModuleFromSelection()) is Declaration candidate) ||
37+
candidate.DeclarationType != DeclarationType.ProceduralModule ||
38+
!candidate.Annotations.Any(annotation => annotation is TestModuleAnnotation) ||
39+
!_engine.CanRun)
40+
{
41+
return;
42+
}
43+
44+
var tests = _engine.Tests.Where(test => test.Declaration.QualifiedModuleName.Equals(candidate.QualifiedModuleName)).ToList();
45+
46+
if (!tests.Any())
47+
{
48+
return;
49+
}
50+
51+
_engine.Run(tests);
52+
}
53+
54+
private Declaration FindModuleFromSelection()
55+
{
56+
var active = _selectionService?.ActiveSelection();
57+
return !active.HasValue
58+
? null
59+
: _finderProvider.DeclarationFinder.ModuleDeclaration(active.Value.QualifiedName);
60+
}
61+
}
62+
}

Rubberduck.Main/Root/RubberduckIoCInstaller.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ private Type[] CodePaneContextMenuItems()
447447
typeof(SmartIndenterParentMenu),
448448
typeof(FindSymbolCommandMenuItem),
449449
typeof(FindAllReferencesCommandMenuItem),
450-
typeof(FindAllImplementationsCommandMenuItem)
450+
typeof(FindAllImplementationsCommandMenuItem),
451+
typeof(RunSelectedTestModuleCommandMenuItem),
452+
typeof(RunSelectedTestMethodCommandMenuItem)
451453
};
452454
}
453455

Rubberduck.Parsing/VBA/DeclarationCaching/DeclarationFinder.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,18 @@ public Declaration FindSelectedDeclaration(ICodePane activeCodePane)
319319
}
320320
}
321321

322+
/// <summary>
323+
/// Finds all declarations contained within the passed selection.
324+
/// </summary>
325+
/// <param name="selection">The QualifiedSelection to find declarations for.</param>
326+
/// <returns>An IEnumerable of matches.</returns>
327+
public IEnumerable<Declaration> FindDeclarationsForSelection(QualifiedSelection selection)
328+
{
329+
return _declarationsBySelection.Keys
330+
.Where(key => key.Contains(selection))
331+
.SelectMany(key => _declarationsBySelection[key]).Distinct();
332+
}
333+
322334
public IEnumerable<Declaration> FreshUndeclared => _newUndeclared.AllValues();
323335

324336
//This does not need a lock because enumerators over a ConcurrentBag uses a snapshot.

Rubberduck.Resources/Menus/RubberduckMenus.Designer.cs

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rubberduck.Resources/Menus/RubberduckMenus.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,4 +238,10 @@
238238
<data name="AddRemoveReferences" xml:space="preserve">
239239
<value>Add/Remove References...</value>
240240
</data>
241+
<data name="ContextMenu_RunSelectedTest" xml:space="preserve">
242+
<value>Run selected test</value>
243+
</data>
244+
<data name="ContextMenu_RunSelectedTestModule" xml:space="preserve">
245+
<value>Run tests in module</value>
246+
</data>
241247
</root>

Rubberduck.VBEEditor/QualifiedSelection.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public QualifiedSelection(QualifiedModuleName qualifiedName, Selection selection
1414

1515
public Selection Selection { get; }
1616

17+
public bool Contains(QualifiedSelection other)
18+
{
19+
return QualifiedName.Equals(other.QualifiedName) && Selection.Contains(other.Selection);
20+
}
21+
1722
public int CompareTo(QualifiedSelection other)
1823
{
1924
return other.QualifiedName == QualifiedName

0 commit comments

Comments
 (0)