Files
LehrerApp/LehrerApp.Desktop.Tests/ExamGradingDialogViewModelTests.cs
T
adminandClaude Opus 5.5 d7898d5678
CI / build-and-test (push) Canceled after 0s
Punkteeingabe: Korrekturmodus wählbar, Bonusaufgaben nicht mitnummeriert
- "Enter springt: Schüler für Schüler / Aufgabe für Aufgabe" im Dialog wählbar und gemerkt;
  Umschalt+Enter zurück, Pfeiltasten springen direkt und öffnen das Eingabefeld
- Tasten per Tunnel-Handler abgefangen, bevor DataGrid/TextBox sie auswerten
- Abwesende Schüler und Aufgaben der anderen Fassung werden übersprungen
- Zellen lesen ihre Zeile aus dem DataContext (recycelte Zeilen schreiben nicht mehr falsch)
- Bonusaufgaben zählen bei der angezeigten Nummerierung nicht mit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:36:12 +02:00

137 lines
5.6 KiB
C#

using LehrerApp.Core.Models;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.ViewModels.Groups;
using Xunit;
namespace LehrerApp.Desktop.Tests;
/// Deckt 1.6.3 (Bulk-Aktion "Rest als abwesend markieren") ab — löst das Nutzer-Feedback, dass
/// Schüler, die nie nachschreiben, sonst dauerhaft eine leere, unberührte Zeile hinterlassen.
public sealed class ExamGradingDialogViewModelTests
{
[Fact]
public void MarkRemainingAbsent_MarktNurUnberührteZeilenAbwesend()
{
var group = new LearningGroup { Name = "Q1 Chemie" };
var studentWithPoints = new Student { FirstName = "Anna", LastName = "Bauer" };
var untouchedStudent1 = new Student { FirstName = "Ben", LastName = "Cordes" };
var untouchedStudent2 = new Student { FirstName = "Cara", LastName = "Diehl" };
var students = new FakeStudents([studentWithPoints, untouchedStudent1, untouchedStudent2]);
var memberships = new FakeMemberships(
[
new GroupMembership { GroupId = group.Id, StudentId = studentWithPoints.Id },
new GroupMembership { GroupId = group.Id, StudentId = untouchedStudent1.Id },
new GroupMembership { GroupId = group.Id, StudentId = untouchedStudent2.Id },
]);
var exam = new Exam
{
GroupId = group.Id,
Title = "Klausur 1",
Tasks = [new ExamTask { Nr = 1, MaxPoints = 10 }],
GradingKey = GradingService.DefaultKey1To6(),
};
var results = new FakeResults();
var vm = new ExamGradingDialogViewModel(results, students, memberships,
new GradingService(), exam, group.Id);
var rowWithPoints = vm.Rows.Single(r => r.StudentId == studentWithPoints.Id);
rowWithPoints.Cells[0].TrySetValue(7);
vm.MarkRemainingAbsentCommand.Execute(null);
Assert.False(rowWithPoints.Absent);
Assert.True(vm.Rows.Single(r => r.StudentId == untouchedStudent1.Id).Absent);
Assert.True(vm.Rows.Single(r => r.StudentId == untouchedStudent2.Id).Absent);
}
[Fact]
public void MarkRemainingAbsent_LässtBereitsAbwesendMarkierteUnverändert()
{
var group = new LearningGroup { Name = "Q1 Chemie" };
var student = new Student { FirstName = "Ben", LastName = "Cordes" };
var students = new FakeStudents([student]);
var memberships = new FakeMemberships(
[new GroupMembership { GroupId = group.Id, StudentId = student.Id }]);
var exam = new Exam { GroupId = group.Id, Title = "Klausur 1", Tasks = [new ExamTask { Nr = 1, MaxPoints = 10 }], GradingKey = GradingService.DefaultKey1To6() };
var vm = new ExamGradingDialogViewModel(new FakeResults(), students, memberships,
new GradingService(), exam, group.Id);
vm.Rows[0].Absent = true;
vm.MarkRemainingAbsentCommand.Execute(null);
Assert.True(vm.Rows[0].Absent);
}
private static ExamGradingDialogViewModel CreateGrid(int studentCount, params ExamTask[] tasks)
{
var group = new LearningGroup { Name = "Q1 Chemie" };
var students = Enumerable.Range(0, studentCount)
.Select(i => new Student { FirstName = "S", LastName = $"N{i}" }).ToList();
var memberships = new FakeMemberships(
students.Select(s => new GroupMembership { GroupId = group.Id, StudentId = s.Id }).ToList());
var exam = new Exam
{
GroupId = group.Id, Title = "Klausur 1", Tasks = [.. tasks],
GradingKey = GradingService.DefaultKey1To6(),
};
return new ExamGradingDialogViewModel(new FakeResults(), new FakeStudents(students), memberships,
new GradingService(), exam, group.Id);
}
private static ExamTask[] ThreeTasks() =>
[
new() { Nr = 1, MaxPoints = 5 },
new() { Nr = 2, MaxPoints = 5 },
new() { Nr = 3, MaxPoints = 5 },
];
[Fact]
public void Enter_SchülerFürSchüler_LäuftDieZeileEntlangUndDannInDieNächste()
{
var vm = CreateGrid(2, ThreeTasks());
Assert.Equal((0, 1), vm.NextCell(0, 0, GradingMove.Next));
Assert.Equal((1, 0), vm.NextCell(0, 2, GradingMove.Next));
Assert.Null(vm.NextCell(1, 2, GradingMove.Next));
Assert.Equal((0, 2), vm.NextCell(1, 0, GradingMove.Previous));
}
[Fact]
public void Enter_AufgabeFürAufgabe_LäuftDieSpalteHinunterUndDannZurNächstenAufgabe()
{
var vm = CreateGrid(2, ThreeTasks());
vm.EntryOrderName = vm.EntryOrderOptions[1];
Assert.Equal((1, 0), vm.NextCell(0, 0, GradingMove.Next));
Assert.Equal((0, 1), vm.NextCell(1, 0, GradingMove.Next));
Assert.Equal((1, 0), vm.NextCell(0, 1, GradingMove.Previous));
}
[Fact]
public void Navigation_ÜberspringtAbwesendeUndAndereFassung()
{
var vm = CreateGrid(3,
new ExamTask { Nr = 1, MaxPoints = 5 },
new ExamTask { Nr = 2, MaxPoints = 5, Variant = ExamVariant.B },
new ExamTask { Nr = 3, MaxPoints = 5 });
vm.Rows[1].Absent = true;
Assert.Equal((0, 2), vm.NextCell(0, 0, GradingMove.Next));
Assert.Equal((0, 2), vm.NextCell(0, 0, GradingMove.Right));
Assert.Equal((2, 0), vm.NextCell(0, 0, GradingMove.Down));
Assert.Null(vm.NextCell(0, 0, GradingMove.Up));
}
[Fact]
public void Spaltenüberschriften_ZählenBonusaufgabenNichtMit()
{
var vm = CreateGrid(1,
new ExamTask { Nr = 1, MaxPoints = 5 },
new ExamTask { Nr = 2, MaxPoints = 2, IsBonus = true },
new ExamTask { Nr = 3, MaxPoints = 5, Title = "Redox" });
Assert.Equal(["Aufgabe 1", "★ Bonus", "2. Redox"], vm.TaskHeaders);
}
}