using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Headless.XUnit;
using IFSRenderer.AvaloniaShell.Controls;
using Xunit;
namespace IFSRenderer.AvaloniaShell.Tests;
/// <summary>
/// The control the whole UI is built from — 55 instances in the canonical app — so a
/// regression here is a regression in every panel at once.
/// </summary>
/// <remarks>
/// Behaviours are asserted against <c>WpfDisplay/Views/ValueSlider.xaml.cs</c>, which is the
/// specification. Where a value is claimed to be canonical, the canonical source says so.
/// </remarks>
public class ValueSliderTests
{
/// <summary>A slider wired the way a panel wires one, recording what it reports.</summary>
private static (ValueSlider Slider, List<double> Changes) Wire(
double increment = 0.1, double? min = null, double? max = null,
double defaultValue = 0, double start = 0)
{
var changes = new List<double>();
var slider = new ValueSlider
{
SliderSettings = new ValueSliderSettings
{
Label = "Test",
Increment = increment,
MinValue = min,
MaxValue = max,
DefaultValue = defaultValue,
ValueChanged = v => changes.Add(v),
},
Value = start,
};
return (slider, changes);
}
[AvaloniaFact]
public void Step_up_and_down_move_by_the_parameters_own_increment()
{
(ValueSlider slider, List<double> changes) = Wire(increment: 0.05, start: 1.0);
ClickButton(slider, "UpButton");
Assert.Equal(1.05, slider.Value, 6);
ClickButton(slider, "DownButton");
ClickButton(slider, "DownButton");
Assert.Equal(0.95, slider.Value, 6);
Assert.Equal(3, changes.Count);
}
[AvaloniaFact]
public void Value_is_clamped_to_the_declared_range()
{
(ValueSlider slider, _) = Wire(increment: 1, min: 0, max: 3, start: 3);
ClickButton(slider, "UpButton");
Assert.Equal(3, slider.Value);
slider.Value = 0;
ClickButton(slider, "DownButton");
Assert.Equal(0, slider.Value);
}
/// <summary>
/// An unbounded parameter must stay unbounded. The port had invented maxima that made
/// values the original could reach unreachable, which is a correctness bug, not a cosmetic
/// one — see the ranges table in labs#3.
/// </summary>
[AvaloniaFact]
public void A_parameter_with_no_declared_maximum_is_not_capped()
{
(ValueSlider slider, _) = Wire(increment: 1, min: 0, start: 100);
for (int i = 0; i < 50; i++)
ClickButton(slider, "UpButton");
Assert.Equal(150, slider.Value);
}
/// <summary>
/// ValueSlider.xaml.cs UpdateLabelFormat: "N0" for a whole increment, "N4" otherwise, so
/// integer parameters do not display as "22.0000".
/// </summary>
[AvaloniaFact]
public void Whole_increments_display_without_decimals()
{
(ValueSlider whole, _) = Wire(increment: 1, start: 22);
(ValueSlider fractional, _) = Wire(increment: 0.01, start: 22);
Assert.Equal("22", Displayed(whole));
Assert.Equal("22.0000", Displayed(fractional));
}
/// <summary>
/// ResetValueCommand: back to the default, or to zero when already there. The comment in
/// the canonical says "Same as in Apophysis".
/// </summary>
[AvaloniaFact]
public void Reset_goes_to_the_default_then_to_zero()
{
(ValueSlider slider, _) = Wire(defaultValue: 1.0, start: 0.35);
Reset(slider);
Assert.Equal(1.0, slider.Value);
Reset(slider);
Assert.Equal(0.0, slider.Value);
}
private static void ClickButton(ValueSlider slider, string name)
{
Button button = slider.FindControl<Button>(name);
Assert.NotNull(button);
button.RaiseEvent(new Avalonia.Interactivity.RoutedEventArgs(Button.ClickEvent));
}
private static void Reset(ValueSlider slider)
{
// Reset is the first item of the control's context menu.
var menu = slider.ContextMenu;
Assert.NotNull(menu);
var items = (Avalonia.Controls.Controls)menu.ItemsSource;
((MenuItem)items[0]).RaiseEvent(
new Avalonia.Interactivity.RoutedEventArgs(MenuItem.ClickEvent));
}
private static string Displayed(ValueSlider slider) =>
slider.FindControl<TextBlock>("ValueText")?.Text;
}