feat(usability): explain booking fields and validate decimal input (U06)

This commit is contained in:
2026-11-02 06:32:32 +01:00
parent 33a8abf5cc
commit 76ba79b63e
12 changed files with 205 additions and 42 deletions
@@ -0,0 +1,51 @@
using Bunit;
using Duempelkas.App.Components.Dialogs;
using Duempelkas.App.Services;
using Duempelkas.App.Services.Models;
using Duempelkas.Domain.Enums;
using Duempelkas.Infrastructure.Services;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Duempelkas.Tests;
public class BookingFormTests
{
[Fact]
public void Form_ExplainsMissingFields_AndDoesNotRoundInvalidInput()
{
using var context = new BunitContext();
context.Services.AddSingleton<IEntryService>(new EntryService(null!));
var dialog = context.Render<AddEntryDialog>(p => p.Add(x => x.AccountName, "Barkasse"));
Assert.Contains("Barkasse", dialog.Find("h5").TextContent);
Assert.Contains("Bitte beschreiben", dialog.Markup);
Assert.True(dialog.Find("button.btn-success").HasAttribute("disabled"));
dialog.Find("#entry-title").Input("Porto");
dialog.Find("#entry-amount").Input("12,501");
Assert.Equal("12,501", dialog.Find("#entry-amount").GetAttribute("value"));
Assert.True(dialog.Find("button.btn-success").HasAttribute("disabled"));
dialog.Find("#entry-amount").Input("-12,50");
Assert.True(dialog.Find("button.btn-success").HasAttribute("disabled"));
dialog.Find("#entry-amount").Input("12,50");
Assert.False(dialog.Find("button.btn-success").HasAttribute("disabled"));
dialog.Find("#entry-date").Input("");
Assert.True(dialog.Find("button.btn-success").HasAttribute("disabled"));
Assert.Contains("Bitte ein gültiges Datum", dialog.Markup);
dialog.Find("#entry-date").Input("2026-01-02");
Assert.False(dialog.Find("button.btn-success").HasAttribute("disabled"));
}
[Fact]
public void Edit_ShowsFixedTypeAndCorrectionPath_AndRerenderKeepsInput()
{
using var context = new BunitContext();
context.Services.AddSingleton<IEntryService>(new EntryService(null!));
var entry = new EntryDto(1, 1, "2026-001", EntryType.Expense, new(2026, 1, 1), "Porto", 4.20m, false, false, null, null, null);
var dialog = context.Render<AddEntryDialog>(p => p.Add(x => x.EditEntry, entry));
Assert.Contains("Ausgabe", dialog.Markup);
Assert.Contains("mit der richtigen Art neu erfassen", dialog.Markup);
dialog.Find("#entry-title").Input("Korrigierter Text");
dialog.Render(p => p.Add(x => x.EditEntry, entry));
Assert.Equal("Korrigierter Text", dialog.Find("#entry-title").GetAttribute("value"));
}
}
@@ -0,0 +1,35 @@
using Duempelkas.App.Services;
using Xunit;
namespace Duempelkas.Tests;
public class BookingInputTests
{
[Theory]
[InlineData("12,50", true)]
[InlineData("12.50", true)]
[InlineData(" 12,50 ", true)]
[InlineData("12,500", false)]
[InlineData("-12,50", false)]
[InlineData("1.234,56", false)]
[InlineData("1,234.56", false)]
[InlineData("12,50 €", false)]
[InlineData("0", false)]
[InlineData("", false)]
[InlineData("9999999999999999999999999999999999999", false)]
public void DecimalInput_HasExplicitRules(string text, bool valid)
{
Assert.Equal(valid, BookingInput.TryAmount(text, out var amount));
if (valid) Assert.Equal(12.50m, amount);
}
[Fact]
public void ServiceValidation_RejectsInvalidData()
{
Assert.Throws<ArgumentException>(() => BookingInput.Validate(default, "Test", 1m));
Assert.Throws<ArgumentException>(() => BookingInput.Validate(DateTime.Today, " ", 1m));
Assert.Throws<ArgumentException>(() => BookingInput.Validate(DateTime.Today, "Test", -1m));
Assert.Throws<ArgumentException>(() => BookingInput.Validate(DateTime.Today, "Test", 1.001m));
Assert.Equal(default, BookingInput.Date("2026-02-30"));
}
}
@@ -42,9 +42,9 @@ public class YearNavigationTests
page.WaitForElement(".summary-section");
page.FindAll("button").Single(b => b.TextContent.Contains("Einnahme oder Ausgabe erfassen")).Click();
var dialog = page.FindComponent<AddEntryDialog>();
dialog.Find("input[type=date]").Change("2026-01-02");
dialog.Find("input[type=text]").Change("Außerhalb");
dialog.Find("input[type=number]").Change("12.50");
dialog.Find("input[type=date]").Input("2026-01-02");
dialog.Find("#entry-title").Input("Außerhalb");
dialog.Find("#entry-amount").Input("12.50");
Assert.Contains("außerhalb", dialog.Markup);
Assert.True(dialog.Find("button.btn-success").HasAttribute("disabled"));
dialog.Find("input[type=checkbox]").Change(true);