Skip to content

Commit de2bdc6

Browse files
committed
Fix status bar, add test run summary, persist expanded state on group changes.
1 parent 8347076 commit de2bdc6

File tree

13 files changed

+403
-85
lines changed

13 files changed

+403
-85
lines changed

Rubberduck.Core/UI/Controls/GroupingGrid.xaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
<Setter Property="Template">
6363
<Setter.Value>
6464
<ControlTemplate>
65-
<Expander Background="WhiteSmoke" Foreground="Black" FontWeight="SemiBold">
65+
<Expander Background="WhiteSmoke" Foreground="Black" FontWeight="SemiBold"
66+
IsExpanded="{Binding InitialExpandedState, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GroupingGrid}}}">
6667
<Expander.Header>
6768
<StackPanel Orientation="Horizontal">
6869
<TextBlock Margin="4"

Rubberduck.Core/UI/Controls/GroupingGrid.xaml.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,21 @@ public partial class GroupingGrid
99
public static readonly DependencyProperty ShowGroupingItemCountProperty =
1010
DependencyProperty.Register("ShowGroupingItemCount", typeof (bool), typeof(GroupingGrid));
1111

12+
public static readonly DependencyProperty InitialExpandedStateProperty =
13+
DependencyProperty.Register("InitialExpandedState", typeof(bool), typeof(GroupingGrid));
14+
1215
public bool ShowGroupingItemCount
1316
{
14-
get => (bool) GetValue(ShowGroupingItemCountProperty);
17+
get => (bool)GetValue(ShowGroupingItemCountProperty);
1518
set => SetValue(ShowGroupingItemCountProperty, value);
1619
}
1720

21+
public bool InitialExpandedState
22+
{
23+
get => (bool)GetValue(InitialExpandedStateProperty);
24+
set => SetValue(InitialExpandedStateProperty, value);
25+
}
26+
1827
public GroupingGrid()
1928
{
2029
InitializeComponent();

Rubberduck.Core/UI/Controls/PersistGroupExpandedStateBehavior.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ namespace Rubberduck.UI.Controls
99
// Courtesy of @aKzenT - this is a heavily refactored implementation of https://stackoverflow.com/a/15924044/4088852
1010
public class PersistGroupExpandedStateBehavior : Behavior<Expander>
1111
{
12+
public static readonly DependencyProperty InitialExpandedStateProperty = DependencyProperty.Register(
13+
"InitialExpandedState",
14+
typeof(bool),
15+
typeof(PersistGroupExpandedStateBehavior),
16+
new PropertyMetadata(default(bool)));
17+
1218
public static readonly DependencyProperty GroupNameProperty = DependencyProperty.Register(
1319
"GroupName",
1420
typeof(object),
@@ -22,6 +28,12 @@ public class PersistGroupExpandedStateBehavior : Behavior<Expander>
2228
typeof(PersistGroupExpandedStateBehavior),
2329
new PropertyMetadata(default(IDictionary<object, bool>)));
2430

31+
public bool InitialExpandedState
32+
{
33+
get => (bool)GetValue(InitialExpandedStateProperty);
34+
set => SetValue(InitialExpandedStateProperty, value);
35+
}
36+
2537
public object GroupName
2638
{
2739
get => GetValue(GroupNameProperty);
@@ -37,7 +49,7 @@ protected override void OnAttached()
3749

3850
if (expanded != null)
3951
{
40-
AssociatedObject.IsExpanded = expanded.Value;
52+
AssociatedObject.IsExpanded = InitialExpandedState;
4153
}
4254

4355
AssociatedObject.Expanded += OnExpanded;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows.Data;
4+
5+
namespace Rubberduck.UI.Converters
6+
{
7+
public class MillisecondToTimeMagnitudeConverter : IValueConverter
8+
{
9+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10+
{
11+
if (!(value is long milliseconds) || milliseconds == 0)
12+
{
13+
return string.Empty;
14+
}
15+
16+
var time = TimeSpan.FromMilliseconds(milliseconds);
17+
18+
if (time.TotalHours >= 1)
19+
{
20+
return $"{FormatAsRoundedFloat(time.TotalHours)} {Resources.UnitTesting.TestExplorer.TestOutcome_DurationHour}";
21+
}
22+
if (time.TotalMinutes >= 1)
23+
{
24+
return $"{FormatAsRoundedFloat(time.TotalMinutes)} {Resources.UnitTesting.TestExplorer.TestOutcome_DurationMinute}";
25+
}
26+
if (time.TotalSeconds >= 1)
27+
{
28+
return $"{FormatAsRoundedFloat(time.TotalSeconds)} {Resources.UnitTesting.TestExplorer.TestOutcome_DurationSecond}";
29+
}
30+
31+
return $"{time.TotalMilliseconds:F0} {Resources.UnitTesting.TestExplorer.TestOutcome_DurationMillisecond}";
32+
}
33+
34+
private string FormatAsRoundedFloat(double duration)
35+
{
36+
var rounded = Math.Round(duration, 2) * 100;
37+
38+
if (rounded % 100 <= 0.001)
39+
{
40+
return $"{duration:F0}";
41+
}
42+
43+
if (rounded % 10 <= 0.001)
44+
{
45+
return $"{duration:F1}";
46+
}
47+
48+
return $"{duration:F2}";
49+
}
50+
51+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
52+
{
53+
throw new NotImplementedException();
54+
}
55+
}
56+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Globalization;
3+
using System.Windows;
4+
using System.Windows.Data;
5+
6+
namespace Rubberduck.UI.Converters
7+
{
8+
public class NonZeroToVisibilityConverter : IValueConverter
9+
{
10+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
11+
{
12+
if (!(value is int input))
13+
{
14+
return Visibility.Collapsed;
15+
}
16+
17+
return input == 0 ? Visibility.Collapsed : Visibility.Visible;
18+
}
19+
20+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
21+
{
22+
throw new NotImplementedException();
23+
}
24+
}
25+
}

Rubberduck.Core/UI/UnitTesting/TestExplorerControl.xaml

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
xmlns:converters="clr-namespace:Rubberduck.UI.Converters"
1212
Language="{UICulture}"
1313
mc:Ignorable="d"
14-
d:DesignHeight="255" d:DesignWidth="455"
14+
d:DesignHeight="400" d:DesignWidth="600"
1515
d:DataContext="{d:DesignInstance local:TestExplorerViewModel}">
1616

1717
<UserControl.Resources>
@@ -47,7 +47,8 @@
4747

4848
<local:TestOutcomeImageSourceConverter x:Key="OutcomeIconConverter" />
4949
<BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
50-
<converters:BoolToHiddenVisibilityConverter x:Key="BoolToCollapsed"/>
50+
<converters:MillisecondToTimeMagnitudeConverter x:Key="FormattedTime" />
51+
<converters:InvertBoolValueConverter x:Key="InvertBoolValue" />
5152

5253
<SolidColorBrush x:Key="ToolBarToggleButtonVerticalBackground" Color="Transparent" />
5354
<SolidColorBrush x:Key="ToolBarButtonHover" Color="#210080FF"/>
@@ -246,12 +247,14 @@
246247
</Trigger>
247248
</Style.Triggers>
248249
</Style>
250+
249251
</UserControl.Resources>
250252

251253
<Grid>
252254
<Grid.RowDefinitions>
253255
<RowDefinition Height="30"/>
254256
<RowDefinition Height="10"/>
257+
<RowDefinition Height="Auto" />
255258
<RowDefinition Height="*" />
256259
</Grid.RowDefinitions>
257260

@@ -279,7 +282,8 @@
279282
<Separator />
280283

281284
<Menu Background="Transparent">
282-
<MenuItem VerticalAlignment="Center" Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_RunMenuButtonText}">
285+
<MenuItem VerticalAlignment="Center" Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_RunMenuButtonText}"
286+
IsEnabled="{Binding Model.IsBusy, Converter={StaticResource InvertBoolValue}}">
283287
<MenuItem.Icon>
284288
<Image Source="{StaticResource RunImage}" Style="{StaticResource ToolbarImageOpacity}" />
285289
</MenuItem.Icon>
@@ -343,7 +347,8 @@
343347
<Separator />
344348

345349
<Menu Background="Transparent">
346-
<MenuItem VerticalAlignment="Center" Header="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=Add}">
350+
<MenuItem VerticalAlignment="Center" Header="{Resx ResxName=Rubberduck.Resources.RubberduckUI, Key=Add}"
351+
IsEnabled="{Binding Model.IsBusy, Converter={StaticResource InvertBoolValue}}">
347352
<MenuItem.Icon>
348353
<Image Source="{StaticResource AddIcon}" Style="{StaticResource ToolbarImageOpacity}" />
349354
</MenuItem.Icon>
@@ -426,11 +431,64 @@
426431
</ProgressBar>
427432

428433
<Border Grid.Row="2" Padding="2">
434+
<StackPanel Orientation="Horizontal">
435+
<StackPanel.Resources>
436+
<converters:NonZeroToVisibilityConverter x:Key="NonZeroToVisibility"/>
437+
<BitmapImage x:Key="IgnoredTestImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/minus-white.png" />
438+
<BitmapImage x:Key="InconclusiveTestImage" UriSource="pack://application:,,,/Rubberduck.Resources;component/Icons/Fugue/exclamation.png" />
439+
<Style x:Key="ResultsTestOutcomeStyle" TargetType="TextBlock" >
440+
<Setter Property="Margin" Value="4,0,12,0"/>
441+
<Setter Property="TextWrapping" Value="WrapWithOverflow"/>
442+
<Setter Property="VerticalAlignment" Value="Center"/>
443+
</Style>
444+
<Style x:Key="TestOutcomeIconStyle" TargetType="Image">
445+
<Setter Property="Height" Value="16"/>
446+
<Setter Property="Width" Value="16" />
447+
<Setter Property="VerticalAlignment" Value="Center"/>
448+
<Setter Property="Margin" Value="3"/>
449+
</Style>
450+
</StackPanel.Resources>
451+
<TextBlock Margin="2" Padding="4,2,4,2" FontWeight="Bold" VerticalAlignment="Center"
452+
Text="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestOutcome_SummaryCaption}"/>
453+
<TextBlock Style="{StaticResource ResultsTestOutcomeStyle}" Text="{Binding Model.LastTestRunSummary, Mode=OneWay}"/>
454+
<StackPanel Orientation="Horizontal" Visibility="{Binding Model.LastTestFailedCount, Mode=OneWay, Converter={StaticResource NonZeroToVisibility}}" >
455+
<Image Style="{StaticResource TestOutcomeIconStyle}" Source="{StaticResource RunFailedTestsImage}" Margin="4,0,4,0"/>
456+
<TextBlock Style="{StaticResource ResultsTestOutcomeStyle}">
457+
<Run Text="{Binding Model.LastTestFailedCount, Mode=OneWay}"/>
458+
<Run Text="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestOutcome_Failed}"/>
459+
</TextBlock>
460+
</StackPanel>
461+
<StackPanel Orientation="Horizontal" Visibility="{Binding Model.LastTestIgnoredCount, Mode=OneWay, Converter={StaticResource NonZeroToVisibility}}">
462+
<Image Style="{StaticResource TestOutcomeIconStyle}" Source="{StaticResource IgnoredTestImage}" />
463+
<TextBlock Style="{StaticResource ResultsTestOutcomeStyle}">
464+
<Run Text="{Binding Model.LastTestIgnoredCount, Mode=OneWay}"/>
465+
<Run Text="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestOutcome_Ignored}"/>
466+
</TextBlock>
467+
</StackPanel>
468+
<StackPanel Orientation="Horizontal" Visibility="{Binding Model.LastTestInconclusiveCount, Mode=OneWay, Converter={StaticResource NonZeroToVisibility}}">
469+
<Image Style="{StaticResource TestOutcomeIconStyle}" Source="{StaticResource InconclusiveTestImage}" />
470+
<TextBlock Style="{StaticResource ResultsTestOutcomeStyle}">
471+
<Run Text="{Binding Model.LastTestInconclusiveCount, Mode=OneWay}" />
472+
<Run Text="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestOutcome_Inconclusive}"/>
473+
</TextBlock>
474+
</StackPanel>
475+
<StackPanel Orientation="Horizontal" Visibility="{Binding Model.LastTestSucceededCount, Mode=OneWay, Converter={StaticResource NonZeroToVisibility}}">
476+
<Image Style="{StaticResource TestOutcomeIconStyle}" Source="{StaticResource RunPassedTestsImage}" />
477+
<TextBlock Style="{StaticResource ResultsTestOutcomeStyle}">
478+
<Run Text="{Binding Model.LastTestSucceededCount, Mode=OneWay}"/>
479+
<Run Text="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestOutcome_Succeeded}"/>
480+
</TextBlock>
481+
</StackPanel>
482+
</StackPanel>
483+
</Border>
484+
485+
<Border Grid.Row="3" Padding="2">
429486
<Grid>
430487
<controls:GroupingGrid x:Name="TestGrid"
431488
ItemsSource="{Binding Tests}"
432489
SelectionMode="Extended"
433-
ShowGroupingItemCount="True">
490+
ShowGroupingItemCount="True"
491+
InitialExpandedState="True">
434492
<DataGrid.Columns>
435493
<DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_Outcome}">
436494
<DataGridTemplateColumn.CellTemplate>
@@ -454,35 +512,35 @@
454512
<DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_QualifiedModuleName}">
455513
<DataGridTemplateColumn.CellTemplate>
456514
<DataTemplate DataType="vm:TestMethodViewModel">
457-
<TextBlock Text="{Binding Method.Declaration.QualifiedModuleName}" />
515+
<TextBlock Text="{Binding Method.Declaration.QualifiedModuleName}" Margin="0,0,4,0"/>
458516
</DataTemplate>
459517
</DataGridTemplateColumn.CellTemplate>
460518
</DataGridTemplateColumn>
461519
<DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_MethodName}">
462520
<DataGridTemplateColumn.CellTemplate>
463521
<DataTemplate DataType="vm:TestMethodViewModel">
464-
<TextBlock Text="{Binding Method.Declaration.IdentifierName}" />
522+
<TextBlock Text="{Binding Method.Declaration.IdentifierName}" Margin="0,0,4,0"/>
465523
</DataTemplate>
466524
</DataGridTemplateColumn.CellTemplate>
467525
</DataGridTemplateColumn>
468526
<DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_CategoryName}">
469527
<DataGridTemplateColumn.CellTemplate>
470528
<DataTemplate DataType="vm:TestMethodViewModel">
471-
<TextBlock Text="{Binding Method.Category.Name}" />
529+
<TextBlock Text="{Binding Method.Category.Name}" Margin="0,0,4,0"/>
472530
</DataTemplate>
473531
</DataGridTemplateColumn.CellTemplate>
474532
</DataGridTemplateColumn>
475533
<DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_Message}">
476534
<DataGridTemplateColumn.CellTemplate>
477535
<DataTemplate DataType="vm:TestMethodViewModel">
478-
<TextBlock Text="{Binding Result.Output}" />
536+
<TextBlock Text="{Binding Result.Output}" Margin="0,0,4,0" />
479537
</DataTemplate>
480538
</DataGridTemplateColumn.CellTemplate>
481539
</DataGridTemplateColumn>
482540
<DataGridTemplateColumn Header="{Resx ResxName=Rubberduck.Resources.UnitTesting.TestExplorer, Key=TestExplorer_Duration}">
483541
<DataGridTemplateColumn.CellTemplate>
484542
<DataTemplate DataType="vm:TestMethodViewModel">
485-
<TextBlock Text="{Binding Result.Duration, StringFormat={}{0}ms}" />
543+
<TextBlock Text="{Binding Result.Duration, Converter={StaticResource FormattedTime}}" TextAlignment="Right" Margin="0,0,4,0" />
486544
</DataTemplate>
487545
</DataGridTemplateColumn.CellTemplate>
488546
</DataGridTemplateColumn>
@@ -523,6 +581,6 @@
523581
</controls:GroupingGrid>
524582
</Grid>
525583
</Border>
526-
<controls:BusyIndicator Grid.Row="2" Width="120" Height="120" Visibility="{Binding Model.IsBusy, Converter={StaticResource BoolToVisibility}}" />
584+
<controls:BusyIndicator Grid.Row="3" Width="120" Height="120" Visibility="{Binding Model.IsRefreshing, Converter={StaticResource BoolToVisibility}}" />
527585
</Grid>
528586
</UserControl>

0 commit comments

Comments
 (0)