OverviewFilesCommitsIssuesDocsReleasesPeers

ifsrenderer-linux / AvaloniaShell.Tests / ValueSliderTests.cs

138 lines · csharp ref:
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;
}