Lesson bekommt dieselbe Anhang-Infrastruktur wie Documentation (Material, Arbeitsblätter, Experimentunterlagen), samt Fix einer Sync-Lücke, die Anhang- Dateibytes bisher nur für Documentation statt generisch übertragen hat (IHasAttachments). Sitzplan-Tab bekommt einen "Plätze mischen"-Button für Klausursitzpläne. Neu: mehrschrittiger Gefährdungsbeurteilungs-Assistent mit optionalem KI-Entwurf (ai-backend/gbu.php) und PDF-Export, Format bewusst als JSON-Anhang statt eigener Datenbank-Entität. Details und Architekturentscheidungen in TODO.md (4.2, 7.1.5, 10.1.8). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
284 lines
9.3 KiB
C#
284 lines
9.3 KiB
C#
using LehrerApp.Core.Models;
|
|
using LehrerApp.Desktop.ViewModels.Groups;
|
|
using Xunit;
|
|
|
|
namespace LehrerApp.Desktop.Tests;
|
|
|
|
public sealed class HazardAssessmentWizardViewModelTests
|
|
{
|
|
private static HazardAssessmentWizardViewModel Build(HazardAssessment? editing = null) => new(editing, "10c");
|
|
|
|
[Fact]
|
|
public void Neu_StartetAufSchritt0OhneKiOhneEditingKennzeichnung()
|
|
{
|
|
var vm = Build();
|
|
|
|
Assert.Equal(0, vm.StepIndex);
|
|
Assert.True(vm.IsBasicsStep);
|
|
Assert.True(vm.IsFirstStep);
|
|
Assert.Equal("Gefährdungsbeurteilung erstellen", vm.DialogTitle);
|
|
Assert.False(vm.CanUseAi);
|
|
}
|
|
|
|
[Fact]
|
|
public void Next_OhneTitel_BleibtAufSchritt0UndSetztFehler()
|
|
{
|
|
var vm = Build();
|
|
|
|
vm.NextCommand.Execute(null);
|
|
|
|
Assert.Equal(0, vm.StepIndex);
|
|
Assert.NotEqual("", vm.TitleError);
|
|
}
|
|
|
|
[Fact]
|
|
public void Next_MitTitel_GehtEinenSchrittWeiterUndAktualisiertStepFlags()
|
|
{
|
|
var vm = Build();
|
|
vm.Title = "Natrium in Wasser";
|
|
|
|
vm.NextCommand.Execute(null);
|
|
|
|
Assert.Equal(1, vm.StepIndex);
|
|
Assert.True(vm.IsSubstancesStep);
|
|
Assert.False(vm.IsFirstStep);
|
|
Assert.Equal("", vm.TitleError);
|
|
}
|
|
|
|
[Fact]
|
|
public void Back_AufSchritt0_BleibtAufSchritt0()
|
|
{
|
|
var vm = Build();
|
|
|
|
vm.BackCommand.Execute(null);
|
|
|
|
Assert.Equal(0, vm.StepIndex);
|
|
}
|
|
|
|
[Fact]
|
|
public void DurchlaufAllerSchritte_LandetAufZusammenfassung()
|
|
{
|
|
var vm = Build();
|
|
vm.Title = "Natrium in Wasser";
|
|
|
|
for (var i = 0; i < vm.StepCount - 1; i++) vm.NextCommand.Execute(null);
|
|
|
|
Assert.True(vm.IsSummaryStep);
|
|
Assert.True(vm.IsLastStep);
|
|
|
|
vm.NextCommand.Execute(null);
|
|
Assert.True(vm.IsSummaryStep, "Weiter auf dem letzten Schritt darf nicht darüber hinausgehen.");
|
|
}
|
|
|
|
[Fact]
|
|
public void AddSubstance_UndRemove_AktualisierenDieListeUndHasSubstances()
|
|
{
|
|
var vm = Build();
|
|
Assert.False(vm.HasSubstances);
|
|
|
|
vm.AddSubstanceCommand.Execute(null);
|
|
Assert.True(vm.HasSubstances);
|
|
var item = vm.Substances[0];
|
|
item.Name = "Natrium";
|
|
item.Amount = "ca. 0,5 g";
|
|
item.PictogramOptions.First(o => o.Value == GhsPictogram.Flammable).IsSelected = true;
|
|
|
|
item.RemoveCommand.Execute(null);
|
|
|
|
Assert.False(vm.HasSubstances);
|
|
Assert.Empty(vm.Substances);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddHazard_TrimmtUndLeertDasEingabefeld()
|
|
{
|
|
var vm = Build();
|
|
|
|
vm.NewHazard = " Verätzungsgefahr ";
|
|
vm.AddHazardCommand.Execute(null);
|
|
|
|
Assert.Equal(["Verätzungsgefahr"], vm.Hazards);
|
|
Assert.Equal("", vm.NewHazard);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddHazard_MitLeeremTextTutNichts()
|
|
{
|
|
var vm = Build();
|
|
|
|
vm.NewHazard = " ";
|
|
vm.AddHazardCommand.Execute(null);
|
|
|
|
Assert.Empty(vm.Hazards);
|
|
}
|
|
|
|
[Fact]
|
|
public void RemoveHazard_EntferntGenauDenGenanntenEintrag()
|
|
{
|
|
var vm = Build();
|
|
vm.NewHazard = "A"; vm.AddHazardCommand.Execute(null);
|
|
vm.NewHazard = "B"; vm.AddHazardCommand.Execute(null);
|
|
|
|
vm.RemoveHazardCommand.Execute("A");
|
|
|
|
Assert.Equal(["B"], vm.Hazards);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddProtectiveMeasure_TrimmtUndLeertDasEingabefeld()
|
|
{
|
|
var vm = Build();
|
|
|
|
vm.NewProtectiveMeasure = " Schutzbrille ";
|
|
vm.AddProtectiveMeasureCommand.Execute(null);
|
|
|
|
Assert.Equal(["Schutzbrille"], vm.ProtectiveMeasures);
|
|
Assert.Equal("", vm.NewProtectiveMeasure);
|
|
}
|
|
|
|
[Fact]
|
|
public void Save_OhneTitel_SetztFehlerUndSpringtZurueckAufSchritt0()
|
|
{
|
|
var vm = Build();
|
|
vm.Title = "Etwas";
|
|
vm.NextCommand.Execute(null); // Schritt 1
|
|
vm.Title = " ";
|
|
|
|
vm.SaveCommand.Execute(null);
|
|
|
|
Assert.Null(vm.Result);
|
|
Assert.Equal(0, vm.StepIndex);
|
|
Assert.NotEqual("", vm.TitleError);
|
|
}
|
|
|
|
[Fact]
|
|
public void Save_BautVollstaendigesErgebnisAusAllenSchritten()
|
|
{
|
|
var vm = Build();
|
|
vm.Title = "Natrium in Wasser";
|
|
vm.GroupLabel = "10c";
|
|
vm.DateText = "12.09.2026";
|
|
vm.KindName = "Lehrerversuch";
|
|
vm.Procedure = "Kurzbeschreibung";
|
|
vm.AddSubstanceCommand.Execute(null);
|
|
vm.Substances[0].Name = "Natrium";
|
|
vm.Substances[0].Amount = "ca. 0,5 g";
|
|
vm.Substances[0].HStatements = "H260";
|
|
vm.Substances[0].PictogramOptions.First(o => o.Value == GhsPictogram.Flammable).IsSelected = true;
|
|
vm.NewHazard = "Verpuffung"; vm.AddHazardCommand.Execute(null);
|
|
vm.NewProtectiveMeasure = "Schutzbrille"; vm.AddProtectiveMeasureCommand.Execute(null);
|
|
vm.FirstAid = "Wasser spülen";
|
|
vm.Disposal = "Abreagieren lassen";
|
|
vm.Notes = "Hinweis";
|
|
|
|
vm.SaveCommand.Execute(null);
|
|
|
|
Assert.NotNull(vm.Result);
|
|
var result = vm.Result!;
|
|
Assert.Equal("Natrium in Wasser", result.Title);
|
|
Assert.Equal("10c", result.GroupLabel);
|
|
Assert.Equal(new DateOnly(2026, 9, 12), result.Date);
|
|
Assert.Equal(ExperimentKind.Lehrerversuch, result.Kind);
|
|
var substance = Assert.Single(result.Substances);
|
|
Assert.Equal("Natrium", substance.Name);
|
|
Assert.Equal([GhsPictogram.Flammable], substance.GhsPictograms);
|
|
Assert.Equal(["Verpuffung"], result.Hazards);
|
|
Assert.Equal(["Schutzbrille"], result.ProtectiveMeasures);
|
|
Assert.Equal("Wasser spülen", result.FirstAid);
|
|
Assert.Equal("Abreagieren lassen", result.Disposal);
|
|
Assert.Equal("Hinweis", result.Notes);
|
|
Assert.False(result.IsAiAssisted);
|
|
}
|
|
|
|
[Fact]
|
|
public void Konstruktor_MitEditingLaedtAlleFelderVor()
|
|
{
|
|
var editing = new HazardAssessment
|
|
{
|
|
Title = "Bestehende GBU",
|
|
GroupLabel = "9a",
|
|
Date = new DateOnly(2026, 1, 15),
|
|
Kind = ExperimentKind.Schuelerversuch,
|
|
Procedure = "Ablauf",
|
|
Substances = [new HazardSubstance { Name = "Salzsäure", GhsPictograms = [GhsPictogram.Corrosive] }],
|
|
Hazards = ["Verätzung"],
|
|
ProtectiveMeasures = ["Handschuhe"],
|
|
FirstAid = "Erste Hilfe Text",
|
|
Disposal = "Entsorgungstext",
|
|
Notes = "Notiz",
|
|
IsAiAssisted = true,
|
|
};
|
|
|
|
var vm = Build(editing);
|
|
|
|
Assert.Equal("Gefährdungsbeurteilung bearbeiten", vm.DialogTitle);
|
|
Assert.Equal("Bestehende GBU", vm.Title);
|
|
Assert.Equal("9a", vm.GroupLabel);
|
|
Assert.Equal("15.01.2026", vm.DateText);
|
|
Assert.Equal("Schülerversuch", vm.KindName);
|
|
Assert.Equal("Ablauf", vm.Procedure);
|
|
var substance = Assert.Single(vm.Substances);
|
|
Assert.Equal("Salzsäure", substance.Name);
|
|
Assert.True(substance.PictogramOptions.Single(o => o.Value == GhsPictogram.Corrosive).IsSelected);
|
|
Assert.Equal(["Verätzung"], vm.Hazards);
|
|
Assert.Equal(["Handschuhe"], vm.ProtectiveMeasures);
|
|
Assert.Equal("Erste Hilfe Text", vm.FirstAid);
|
|
Assert.Equal("Entsorgungstext", vm.Disposal);
|
|
Assert.Equal("Notiz", vm.Notes);
|
|
Assert.True(vm.IsAiAssisted);
|
|
}
|
|
}
|
|
|
|
public sealed class ExperimentKindDisplayTests
|
|
{
|
|
[Theory]
|
|
[InlineData(ExperimentKind.Lehrerversuch, "Lehrerversuch")]
|
|
[InlineData(ExperimentKind.Schuelerversuch, "Schülerversuch")]
|
|
[InlineData(ExperimentKind.Demonstrationsversuch, "Demonstrationsversuch")]
|
|
public void Label_UndFromLabel_SindZueinanderInvers(ExperimentKind kind, string label)
|
|
{
|
|
Assert.Equal(label, ExperimentKindDisplay.Label(kind));
|
|
Assert.Equal(kind, ExperimentKindDisplay.FromLabel(label));
|
|
}
|
|
|
|
[Fact]
|
|
public void FromLabel_UnbekanntFaelltAufLehrerversuchZurueck() =>
|
|
Assert.Equal(ExperimentKind.Lehrerversuch, ExperimentKindDisplay.FromLabel("Unbekannt"));
|
|
|
|
[Theory]
|
|
[InlineData("Schuelerversuch", ExperimentKind.Schuelerversuch)]
|
|
[InlineData("Demonstrationsversuch", ExperimentKind.Demonstrationsversuch)]
|
|
[InlineData("etwas anderes", ExperimentKind.Lehrerversuch)]
|
|
public void FromWireValue_LiestDenUnmarkiertenEnumNamenDerKiAntwort(string wire, ExperimentKind expected) =>
|
|
Assert.Equal(expected, ExperimentKindDisplay.FromWireValue(wire));
|
|
}
|
|
|
|
public sealed class GhsPictogramDisplayTests
|
|
{
|
|
[Theory]
|
|
[InlineData(GhsPictogram.Explosive, "GHS01")]
|
|
[InlineData(GhsPictogram.Corrosive, "GHS05")]
|
|
[InlineData(GhsPictogram.Environmental, "GHS09")]
|
|
public void Code_LiefertDenErwartetenGhsCode(GhsPictogram value, string code) =>
|
|
Assert.Equal(code, GhsPictogramDisplay.Code(value));
|
|
|
|
[Fact]
|
|
public void FromCode_IstFallUnabhaengigUndTrimmt() =>
|
|
Assert.Equal(GhsPictogram.Corrosive, GhsPictogramDisplay.FromCode(" ghs05 "));
|
|
|
|
[Fact]
|
|
public void FromCode_UnbekannterCodeLiefertNull() =>
|
|
Assert.Null(GhsPictogramDisplay.FromCode("GHS99"));
|
|
|
|
[Fact]
|
|
public void BuildOptions_MarkiertNurDieUebergebenenAlsAusgewaehlt()
|
|
{
|
|
var options = GhsPictogramDisplay.BuildOptions([GhsPictogram.Toxic, GhsPictogram.Flammable]);
|
|
|
|
Assert.Equal(9, options.Count);
|
|
Assert.True(options.Single(o => o.Value == GhsPictogram.Toxic).IsSelected);
|
|
Assert.True(options.Single(o => o.Value == GhsPictogram.Flammable).IsSelected);
|
|
Assert.False(options.Single(o => o.Value == GhsPictogram.Corrosive).IsSelected);
|
|
}
|
|
}
|