Files
LehrerApp/LehrerApp.Desktop.Tests/LessonDialogViewModelTests.cs
T
adminandClaude Sonnet 5 ee1a47641e Stundenverlaufsplan: Viewer und Alternativpfad-Katalog (Kapitel 4.2 Nachtrag)
Schreibgeschützter LessonViewerDialog (größere Schrift, ohne Bearbeitungs-/Verlängern-/
Verschieben-Funktion) für den Einsatz während des Unterrichtens, erreichbar über "Anzeigen"
im Stunden-Toolbar.

Alternative Unterrichtsabläufe (z.B. Kurzversion bei Zeitmangel) laufen jetzt über einen
echten Katalog (neues Modell AlternativeLessonPath: Name + Beschreibung) statt Freitext direkt
an der Phase: im Verlaufsplan-Editor eine kompakte, farbig unterstützte Checkbox statt einer
durchgehend sichtbaren Eingabespalte, Zuordnung/Neuanlage über einen eigenen Dialog. Der Viewer
gruppiert Phasen entsprechend und zeigt die hinterlegte Beschreibung. Schema-Migration v3→v4
führt bestehende Freitextwerte verlustfrei in Katalogeinträge über.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 01:53:12 +02:00

239 lines
8.5 KiB
C#

using LehrerApp.Core.Models;
using LehrerApp.Desktop.ViewModels.Groups;
using Xunit;
namespace LehrerApp.Desktop.Tests;
public sealed class LessonDialogViewModelTests
{
private static LessonDialogViewModel BuildVm(Guid unitId, Guid groupId, Lesson? editing = null) =>
new(new FakeLessons(), new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
unitId, groupId, [], [], editing);
[Fact]
public void AddPhase_FuegtZeileMitStandardwertenHinzu()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.AddPhaseCommand.Execute(null);
Assert.Single(vm.Phases);
Assert.Equal(5, vm.Phases[0].DurationMinutes);
Assert.False(vm.Phases[0].HasAlternativePath);
}
[Fact]
public void RemovePhase_EntferntZeileUndAktualisiertGesamtdauer()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].DurationMinutes = 20;
vm.Phases[0].RemoveCommand.Execute(null);
Assert.Empty(vm.Phases);
Assert.Equal("0 Minuten gesamt", vm.TotalDurationDisplay);
}
[Fact]
public void MoveDown_VertauschtReihenfolgeDerPhasen()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].Name = "Einstieg";
vm.AddPhaseCommand.Execute(null);
vm.Phases[1].Name = "Erarbeitung";
vm.Phases[0].MoveDownCommand.Execute(null);
Assert.Equal("Erarbeitung", vm.Phases[0].Name);
Assert.Equal("Einstieg", vm.Phases[1].Name);
}
[Fact]
public void Shorthand_IstFreitextOhneErzwungeneVonNachStruktur()
{
// Kurzsymbol muss nicht immer ein Materialfluss-Pfeil sein — manchmal nur eine
// Sozialform ohne Pfeil (z.B. "Plenum", "LDE").
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.AddPhaseCommand.Execute(null);
var phase = vm.Phases[0];
phase.Shorthand = "Plenum";
Assert.Equal("Plenum", phase.Shorthand);
phase.Shorthand = "AB001->S";
Assert.Equal("AB001->S", phase.Shorthand);
}
[Fact]
public void ShorthandSuggestions_KombiniertKatalogUndBisherigeStundenwerte()
{
var codes = new FakeShorthandCodes([new ShorthandCode { Code = "Tb" }, new ShorthandCode { Code = "SH" }]);
var vm = new LessonDialogViewModel(new FakeLessons(), codes, new FakeAlternativeLessonPaths([]),
Guid.NewGuid(), Guid.NewGuid(), [], ["Plenum", "LDE", "Tb"], null); // "Tb" doppelt (Katalog + Historie), soll nur einmal erscheinen
Assert.Equal(["LDE", "Plenum", "SH", "Tb"], vm.ShorthandSuggestions);
}
[Fact]
public void RecomputeTimes_LeitetUhrzeitJePhaseAusBeginnUndKumulierterDauerAb()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.StartTimeText = "12:00";
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].DurationMinutes = 15;
vm.AddPhaseCommand.Execute(null);
vm.Phases[1].DurationMinutes = 10;
Assert.Equal("ab 12:00", vm.Phases[0].ComputedTimeDisplay);
Assert.Equal("ab 12:15", vm.Phases[1].ComputedTimeDisplay);
Assert.Equal("25 Minuten gesamt", vm.TotalDurationDisplay);
}
[Fact]
public void RecomputeTimes_OhneBeginnBleibtDieUhrzeitanzeigeLeer()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].DurationMinutes = 15;
Assert.Equal("", vm.Phases[0].ComputedTimeDisplay);
}
[Fact]
public void Save_ErfordertThemaUndGueltigesDatum()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.Topic = ""; vm.DateText = "keinDatum";
vm.SaveCommand.Execute(null);
Assert.Null(vm.Result);
Assert.NotEqual("", vm.TopicError);
Assert.NotEqual("", vm.DateTextError);
}
[Fact]
public void Save_UngueltigesBeginnFormatWirdAbgelehnt()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.Topic = "Thema"; vm.DateText = "01.09.2025"; vm.StartTimeText = "nicht-uhrzeit";
vm.SaveCommand.Execute(null);
Assert.Null(vm.Result);
Assert.NotEqual("", vm.StartTimeTextError);
}
[Fact]
public void Save_SetztUnitIdGroupIdUndUebernimmtPhasenUndBeginn()
{
var unitId = Guid.NewGuid();
var groupId = Guid.NewGuid();
var lessons = new FakeLessons();
var vm = new LessonDialogViewModel(lessons, new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]),
unitId, groupId, [], [], null)
{
Topic = "Brechung", DateText = "01.09.2025", StartTimeText = "11:45",
};
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].Name = "Einstieg";
vm.Phases[0].DurationMinutes = 10;
vm.Phases[0].Activity = "Begrüßung";
vm.Phases[0].Material = "AB01";
vm.Phases[0].Shorthand = "L->AB01";
vm.SaveCommand.Execute(null);
Assert.NotNull(vm.Result);
Assert.Equal(unitId, vm.Result!.UnitId);
Assert.Equal(groupId, vm.Result.GroupId);
Assert.Equal(new TimeOnly(11, 45), vm.Result.StartTime);
Assert.Single(vm.Result.Phases);
Assert.Equal("Einstieg", vm.Result.Phases[0].Name);
Assert.Equal(10, vm.Result.Phases[0].DurationMinutes);
Assert.Equal("L->AB01", vm.Result.Phases[0].Shorthand);
Assert.Single(lessons.GetByUnit(unitId));
}
[Fact]
public void HasAlternativePath_AngehaktOeffnetZuweisungUndUebernimmtErgebnis()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
var path = new AlternativeLessonPath { Name = "Kurzversion" };
vm.OnPickAlternativePath = _ => Task.FromResult<AlternativeLessonPath?>(path);
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].HasAlternativePath = true;
Assert.True(vm.Phases[0].HasAlternativePath);
Assert.Equal("Kurzversion", vm.Phases[0].AlternativePathName);
Assert.Equal(path.Id, vm.Phases[0].AlternativePathId);
}
[Fact]
public void HasAlternativePath_AbbruchDerZuweisungLaesstEsUnangehakt()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
vm.OnPickAlternativePath = _ => Task.FromResult<AlternativeLessonPath?>(null); // abgebrochen
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].HasAlternativePath = true;
Assert.False(vm.Phases[0].HasAlternativePath);
Assert.Null(vm.Phases[0].AlternativePathId);
}
[Fact]
public void HasAlternativePath_AbwaehlenEntferntZuordnungOhneDialogZuOeffnen()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
var path = new AlternativeLessonPath { Name = "Kurzversion" };
var dialogCalls = 0;
vm.OnPickAlternativePath = _ => { dialogCalls++; return Task.FromResult<AlternativeLessonPath?>(path); };
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].HasAlternativePath = true; // zuweisen -> 1 Dialogaufruf
vm.Phases[0].HasAlternativePath = false; // abwählen -> kein weiterer Dialogaufruf
Assert.Equal(1, dialogCalls);
Assert.False(vm.Phases[0].HasAlternativePath);
Assert.Equal("", vm.Phases[0].AlternativePathName);
Assert.Null(vm.Phases[0].AlternativePathId);
}
[Fact]
public void Save_UebernimmtZugewiesenenAlternativePfad()
{
var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid());
var path = new AlternativeLessonPath { Name = "Kurzversion" };
vm.OnPickAlternativePath = _ => Task.FromResult<AlternativeLessonPath?>(path);
vm.Topic = "Thema"; vm.DateText = "01.09.2025";
vm.AddPhaseCommand.Execute(null);
vm.Phases[0].HasAlternativePath = true;
vm.SaveCommand.Execute(null);
Assert.Equal(path.Id, vm.Result!.Phases[0].AlternativePathId);
}
[Fact]
public void AddPhaseInternal_LoestBestehendeZuordnungBeimBearbeitenAuf()
{
var path = new AlternativeLessonPath { Name = "Kurzversion" };
var alternativePaths = new FakeAlternativeLessonPaths([path]);
var editing = new Lesson
{
Topic = "Thema", Date = new DateOnly(2025, 9, 1),
Phases = [new LessonPhaseStep { Name = "Einstieg", AlternativePathId = path.Id }],
};
var vm = new LessonDialogViewModel(new FakeLessons(), new FakeShorthandCodes([]), alternativePaths,
Guid.NewGuid(), Guid.NewGuid(), [], [], editing);
Assert.True(vm.Phases[0].HasAlternativePath);
Assert.Equal("Kurzversion", vm.Phases[0].AlternativePathName);
}
}