Skip to content

Commit f234fb9

Browse files
committed
Add filtering to TestExplorer
1 parent c02c17d commit f234fb9

File tree

5 files changed

+164
-43
lines changed

5 files changed

+164
-43
lines changed

Rubberduck.Core/UI/UnitTesting/TestExplorerControl.xaml

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@
4949

5050
<BitmapImage x:Key="CopyResultsImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/document-copy.png" />
5151

52+
<BitmapImage x:Key="FilterImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/Funnel.png" />
53+
<BitmapImage x:Key="OutcomeUnknown" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/question-white.png" />
54+
<BitmapImage x:Key="OutcomeSpectacularFail" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/skull-mad.png" />
55+
<BitmapImage x:Key="OutcomeFail" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/cross-circle.png" />
56+
<BitmapImage x:Key="OutcomeInconclusive" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/exclamation.png" />
57+
<BitmapImage x:Key="OutcomeSucceeded" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/tick-circle.png" />
58+
5259
<local:TestOutcomeImageSourceConverter x:Key="OutcomeIconConverter" />
5360
<converters:MillisecondToTimeMagnitudeConverter x:Key="FormattedTime" />
5461
<converters:InvertBoolValueConverter x:Key="InvertBoolValue" />
@@ -82,6 +89,7 @@
8289
<local:TestExplorerGroupingBooleanConverter x:Key="OutcomeConverter" />
8390
<local:TestExplorerGroupingBooleanConverter x:Key="LocationConverter" />
8491
<local:TestExplorerGroupingBooleanConverter x:Key="CategoryConverter" />
92+
<local:TestExplorerOutcomeFilterToBooleanConverter x:Key="OutcomeFilterConverter" />
8593
<Style x:Key="ToolBarToggleStyle" TargetType="ToggleButton">
8694
<Setter Property="Margin" Value="2" />
8795
<Setter Property="BorderBrush" Value="{x:Static SystemColors.ActiveBorderBrush}" />
@@ -216,15 +224,38 @@
216224

217225
<Separator />
218226

227+
<Image Source="{StaticResource FilterImage}" />
228+
219229
<Label Content="{Resx Key=TestExplorer_Filter, ResxName=Rubberduck.Resources.UnitTesting.TestExplorer}" />
220230
<controls:SearchBox Width="100"
221231
Text="{Binding TestNameFilter, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
222232

223-
<Label Content="{Resx Key=TestExplorer_Outcome, ResxName=Rubberduck.Resources.UnitTesting.TestExplorer}" />
224-
225-
<ComboBox Width="100"
226-
ItemsSource="{Binding OutcomeFilters, UpdateSourceTrigger=PropertyChanged}"
227-
SelectedItem="{Binding SelectedOutcomeFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
233+
<Label Content="{Resx Key=TestExplorer_Outcome, ResxName=Rubberduck.Resources.UnitTesting.TestExplorer}" />
234+
<ToggleButton Style="{StaticResource ToolBarToggleStyle}"
235+
ToolTip="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=TestOutcome_Unknown}"
236+
IsChecked="{Binding Path=OutcomeFilter, Converter={StaticResource OutcomeFilterConverter}, ConverterParameter={x:Static local:TestExplorerOutcomeFilter.Unknown}}" >
237+
<Image Source="{StaticResource OutcomeUnknown}" />
238+
</ToggleButton>
239+
<ToggleButton Style="{StaticResource ToolBarToggleStyle}"
240+
ToolTip="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=TestOutcome_SpectacularFail}"
241+
IsChecked="{Binding Path=OutcomeFilter, Converter={StaticResource OutcomeFilterConverter}, ConverterParameter={x:Static local:TestExplorerOutcomeFilter.SpectacularFail}}" >
242+
<Image Source="{StaticResource OutcomeSpectacularFail}" />
243+
</ToggleButton>
244+
<ToggleButton Style="{StaticResource ToolBarToggleStyle}"
245+
ToolTip="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=TestOutcome_Fail}"
246+
IsChecked="{Binding Path=OutcomeFilter, Converter={StaticResource OutcomeFilterConverter}, ConverterParameter={x:Static local:TestExplorerOutcomeFilter.Fail}}" >
247+
<Image Source="{StaticResource OutcomeFail}" />
248+
</ToggleButton>
249+
<ToggleButton Style="{StaticResource ToolBarToggleStyle}"
250+
ToolTip="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=TestOutcome_Inconclusive}"
251+
IsChecked="{Binding Path=OutcomeFilter, Converter={StaticResource OutcomeFilterConverter}, ConverterParameter={x:Static local:TestExplorerOutcomeFilter.Inconclusive}}" >
252+
<Image Source="{StaticResource OutcomeInconclusive}" />
253+
</ToggleButton>
254+
<ToggleButton Style="{StaticResource ToolBarToggleStyle}"
255+
ToolTip="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=TestOutcome_Succeeded}"
256+
IsChecked="{Binding Path=OutcomeFilter, Converter={StaticResource OutcomeFilterConverter}, ConverterParameter={x:Static local:TestExplorerOutcomeFilter.Succeeded}}" >
257+
<Image Source="{StaticResource OutcomeSucceeded}" />
258+
</ToggleButton>
228259

229260
<Separator />
230261

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using System.Windows.Data;
8+
9+
namespace Rubberduck.UI.UnitTesting
10+
{
11+
class TestExplorerOutcomeFilterToBooleanConverter : IValueConverter
12+
{
13+
private TestExplorerOutcomeFilter _cachedOutcomeFilter;
14+
15+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
16+
{
17+
if (!(parameter is TestExplorerOutcomeFilter outcomeParameter)
18+
|| !(value is TestExplorerOutcomeFilter outcomeCurrentlyFiltering))
19+
{
20+
return false;
21+
}
22+
23+
_cachedOutcomeFilter = outcomeCurrentlyFiltering;
24+
return _cachedOutcomeFilter.HasFlag(outcomeParameter);
25+
}
26+
27+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
28+
{
29+
if (!(parameter is TestExplorerOutcomeFilter outcomeParameter)
30+
|| !(value is bool isApplied))
31+
{
32+
return _cachedOutcomeFilter;
33+
}
34+
35+
_cachedOutcomeFilter = isApplied
36+
? _cachedOutcomeFilter | outcomeParameter
37+
: _cachedOutcomeFilter ^ outcomeParameter;
38+
return _cachedOutcomeFilter;
39+
}
40+
}
41+
}

Rubberduck.Core/UI/UnitTesting/TestExplorerViewModel.cs

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using Rubberduck.VBEditor.Utility;
2020
using DataFormats = System.Windows.DataFormats;
2121
using Rubberduck.Resources.UnitTesting;
22+
using Rubberduck.UI.Converters;
2223

2324
namespace Rubberduck.UI.UnitTesting
2425
{
@@ -30,6 +31,18 @@ public enum TestExplorerGrouping
3031
Location
3132
}
3233

34+
[Flags]
35+
public enum TestExplorerOutcomeFilter
36+
{
37+
None = 0,
38+
Unknown = 1,
39+
SpectacularFail = 1 << 1,
40+
Fail = 1 << 2,
41+
Inconclusive = 1 << 3,
42+
Succeeded = 1 << 4,
43+
All = Unknown | SpectacularFail | Fail | Inconclusive | Succeeded
44+
}
45+
3346
internal sealed class TestExplorerViewModel : ViewModelBase, INavigateSelection, IDisposable
3447
{
3548
private readonly IClipboardWriter _clipboard;
@@ -69,11 +82,7 @@ public TestExplorerViewModel(ISelectionService selectionService,
6982
OnPropertyChanged(nameof(Tests));
7083
TestGrouping = TestExplorerGrouping.Outcome;
7184

72-
73-
OutcomeFilters = new System.Collections.ObjectModel.ObservableCollection<object>(
74-
new[] { _allResultsFilter }
75-
.Concat(Enum.GetNames(typeof(TestOutcome)).Select(s => s.ToString()))
76-
.OrderBy(s => s));
85+
OutcomeFilter = TestExplorerOutcomeFilter.All;
7786
}
7887

7988
public TestExplorerModel Model { get; }
@@ -139,24 +148,21 @@ public TestExplorerGrouping TestGrouping
139148
}
140149
}
141150

142-
//public System.Collections.ObjectModel.ObservableCollection<System.Windows.Media.Imaging.BitmapImage> OutcomeFilters { get; }
143-
public System.Collections.ObjectModel.ObservableCollection<object> OutcomeFilters { get; }
144-
145-
private TestOutcome? _filtering = null;
146-
147-
public TestOutcome? TestFiltering
151+
private TestExplorerOutcomeFilter _outcomeFilter = TestExplorerOutcomeFilter.All;
152+
public TestExplorerOutcomeFilter OutcomeFilter
148153
{
149-
get => _filtering;
154+
get => _outcomeFilter;
150155
set
151156
{
152-
if (value == _filtering)
157+
if (value == _outcomeFilter)
153158
{
154159
return;
155160
}
156161

157-
_filtering = value;
158-
Tests.Refresh();
162+
_outcomeFilter = value;
159163
OnPropertyChanged();
164+
165+
Tests.Filter = FilterResults;
160166
}
161167
}
162168

@@ -186,36 +192,20 @@ public bool ExpandedState
186192
OnPropertyChanged();
187193
}
188194
}
189-
190-
private static readonly string _allResultsFilter = TestExplorer.ResourceManager.GetString("TestExplorer_AllResults", CultureInfo.CurrentUICulture);
191-
private string _selectedOutcomeFilter = _allResultsFilter;
192-
public string SelectedOutcomeFilter
193-
{
194-
get => _selectedOutcomeFilter;
195-
set
196-
{
197-
if (_selectedOutcomeFilter != value)
198-
{
199-
_selectedOutcomeFilter = value.Replace(" ", string.Empty);
200-
OnPropertyChanged();
201-
Tests.Filter = FilterResults;
202-
OnPropertyChanged(nameof(Tests));
203-
}
204-
}
205-
}
206-
207195
/// <summary>
208196
/// Filtering for displaying the correct tests.
209-
/// Uses both <see cref="SelectedOutcomeFilter"/> and <see cref="TestNameFilter"/>
197+
/// Uses both <see cref="OutcomeFilter"/> and <see cref="TestNameFilter"/>
210198
/// </summary>
211199
private bool FilterResults(object unitTest)
212200
{
213201
var testMethodViewModel = unitTest as TestMethodViewModel;
214-
var memberName = testMethodViewModel.QualifiedName.MemberName;
215202

216-
return memberName.ToUpper().Contains(TestNameFilter?.ToUpper() ?? string.Empty)
217-
&& (SelectedOutcomeFilter.Equals(_allResultsFilter) || testMethodViewModel.Result.Outcome.ToString().Equals(_selectedOutcomeFilter));
203+
var passesNameFilter = testMethodViewModel.QualifiedName.MemberName.ToUpper().Contains(TestNameFilter?.ToUpper() ?? string.Empty);
204+
205+
Enum.TryParse(testMethodViewModel.Result.Outcome.ToString(), out TestExplorerOutcomeFilter convertedOutcome);
206+
var passesOutcomeFilter = (OutcomeFilter & convertedOutcome) == convertedOutcome;
218207

208+
return passesNameFilter && passesOutcomeFilter;
219209
}
220210

221211
private void HandleTestCompletion(object sender, TestCompletedEventArgs e)

Rubberduck.Resources/RubberduckUI.Designer.cs

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rubberduck.Resources/RubberduckUI.resx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1548,7 +1548,6 @@ NOTE: Restart is required for the setting to take effect.</value>
15481548
</data>
15491549
<data name="RefactoringFailure_BaseMessage" xml:space="preserve">
15501550
<value>Refactoring failed.</value>
1551-
15521551
</data>
15531552
<data name="RefactoringFailure_NoActiveSelection" xml:space="preserve">
15541553
<value>There is no active selection.</value>
@@ -1594,4 +1593,19 @@ NOTE: Restart is required for the setting to take effect.</value>
15941593
<value>Declaration type of target '{0}' is '{1}' instead of one of the expected '{2}'.</value>
15951594
<comment>{0}: name of target; {1}: actual declaration type; {2}: expected declaration types</comment>
15961595
</data>
1596+
<data name="TestOutcome_Fail" xml:space="preserve">
1597+
<value>Fail</value>
1598+
</data>
1599+
<data name="TestOutcome_Inconclusive" xml:space="preserve">
1600+
<value>Inconclusive</value>
1601+
</data>
1602+
<data name="TestOutcome_SpectacularFail" xml:space="preserve">
1603+
<value>SpectacularFail</value>
1604+
</data>
1605+
<data name="TestOutcome_Succeeded" xml:space="preserve">
1606+
<value>Suceeded</value>
1607+
</data>
1608+
<data name="TestOutcome_Unknown" xml:space="preserve">
1609+
<value>Unknown</value>
1610+
</data>
15971611
</root>

0 commit comments

Comments
 (0)