diff --git a/src/LageBuch.App.Shared/Theme/Styles.axaml b/src/LageBuch.App.Shared/Theme/Styles.axaml
index 0cfb86e..ad9caf0 100644
--- a/src/LageBuch.App.Shared/Theme/Styles.axaml
+++ b/src/LageBuch.App.Shared/Theme/Styles.axaml
@@ -250,6 +250,21 @@
+
+
+
diff --git a/src/LageBuch.App.Shared/Views/CoMessprotokollView.axaml b/src/LageBuch.App.Shared/Views/CoMessprotokollView.axaml
index 498a342..de32195 100644
--- a/src/LageBuch.App.Shared/Views/CoMessprotokollView.axaml
+++ b/src/LageBuch.App.Shared/Views/CoMessprotokollView.axaml
@@ -3,9 +3,14 @@
xmlns:vm="clr-namespace:LageBuch.AppLogic.ViewModels;assembly=LageBuch.AppLogic"
xmlns:conv="clr-namespace:LageBuch.App.Shared.Converters"
xmlns:dom="clr-namespace:LageBuch.Domain.CoMeasurement;assembly=LageBuch.Domain"
+ xmlns:md="clr-namespace:LageBuch.Persistence.MasterData;assembly=LageBuch.Persistence"
x:Class="LageBuch.App.Shared.Views.CoMessprotokollView"
x:DataType="vm:CoMessprotokollViewModel">
+
+
+
-
+
@@ -94,15 +100,8 @@
Padding="16"
IsEnabled="{Binding !IsReadOnly}">
-
-
-
-
-
+
+
@@ -122,20 +121,19 @@
+ FormatString="0" PlaceholderText="Kein Messwert" ShowButtonSpinner="False" />
-
-
+
+
-
-
-
-
-
+
+
diff --git a/src/LageBuch.App.Shared/Views/EtbView.axaml b/src/LageBuch.App.Shared/Views/EtbView.axaml
index 2d98fc6..faaba3f 100644
--- a/src/LageBuch.App.Shared/Views/EtbView.axaml
+++ b/src/LageBuch.App.Shared/Views/EtbView.axaml
@@ -3,11 +3,12 @@
xmlns:vm="clr-namespace:LageBuch.AppLogic.ViewModels;assembly=LageBuch.AppLogic"
xmlns:b="clr-namespace:LageBuch.App.Shared.Behaviors"
xmlns:etb="clr-namespace:LageBuch.Domain.Etb;assembly=LageBuch.Domain"
+ xmlns:md="clr-namespace:LageBuch.Persistence.MasterData;assembly=LageBuch.Persistence"
x:Class="LageBuch.App.Shared.Views.EtbView"
x:DataType="vm:EtbViewModel">
-
+
@@ -18,33 +19,46 @@
Background="#0F141D" BorderBrush="{StaticResource HairlineBrush}" BorderThickness="1"
CornerRadius="{StaticResource RadiusMd}" Padding="14">
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Command="{Binding AddEntryAndCreateTaskCommand}"
+ VerticalAlignment="Bottom" Margin="0,0,0,1" />
@@ -55,13 +69,18 @@
CornerRadius="{StaticResource RadiusMd}" Padding="14"
IsVisible="{Binding IsEditing}">
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/LageBuch.App.Shared/Views/FilesView.axaml b/src/LageBuch.App.Shared/Views/FilesView.axaml
index 1e4579e..bf9ee98 100644
--- a/src/LageBuch.App.Shared/Views/FilesView.axaml
+++ b/src/LageBuch.App.Shared/Views/FilesView.axaml
@@ -1,11 +1,12 @@
-
+
@@ -24,15 +25,40 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
-
-
+
+
diff --git a/src/LageBuch.App.Shared/Views/ForcesView.axaml b/src/LageBuch.App.Shared/Views/ForcesView.axaml
index 2d52575..461e460 100644
--- a/src/LageBuch.App.Shared/Views/ForcesView.axaml
+++ b/src/LageBuch.App.Shared/Views/ForcesView.axaml
@@ -7,7 +7,7 @@
x:DataType="vm:ForcesViewModel">
-
+
@@ -30,19 +30,18 @@
-
-
-
+
-
+
-
-
+
@@ -52,41 +51,39 @@
-
+
-
-
-
+
-
-
-
+
+
+ 0 und lässt den Platzhalter stehen. Kein Beispiel-Platzhalter: das Label sagt bereits,
+ was gezählt wird, eine erfundene Zahl könnte für eine echte gehalten werden. -->
-
-
-
-
-
-
+
+
-
-
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
diff --git a/src/LageBuch.App.Shared/Views/MasterDataEditorView.axaml b/src/LageBuch.App.Shared/Views/MasterDataEditorView.axaml
index 32940e2..b07efcb 100644
--- a/src/LageBuch.App.Shared/Views/MasterDataEditorView.axaml
+++ b/src/LageBuch.App.Shared/Views/MasterDataEditorView.axaml
@@ -1,6 +1,7 @@
@@ -176,13 +177,22 @@
+
+
+
+
+
-
-
+
+
@@ -210,6 +220,12 @@
+
+
+
+
+
+
@@ -217,11 +233,13 @@
-
-
@@ -255,16 +273,34 @@
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
diff --git a/src/LageBuch.App.Shared/Views/OperatorPromptView.axaml b/src/LageBuch.App.Shared/Views/OperatorPromptView.axaml
index 5edb1c1..3d52553 100644
--- a/src/LageBuch.App.Shared/Views/OperatorPromptView.axaml
+++ b/src/LageBuch.App.Shared/Views/OperatorPromptView.axaml
@@ -1,6 +1,7 @@
@@ -35,7 +36,9 @@
-
+
@@ -44,7 +47,8 @@
-
-
+
@@ -20,21 +21,38 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -55,16 +73,27 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -87,7 +116,8 @@
-
diff --git a/src/LageBuch.App.Shared/Views/ScbaView.axaml b/src/LageBuch.App.Shared/Views/ScbaView.axaml
index e6cedf0..9361001 100644
--- a/src/LageBuch.App.Shared/Views/ScbaView.axaml
+++ b/src/LageBuch.App.Shared/Views/ScbaView.axaml
@@ -1,6 +1,7 @@
@@ -37,35 +38,58 @@
Background="#0F141D" BorderBrush="{StaticResource HairlineBrush}" BorderThickness="1"
CornerRadius="{StaticResource RadiusMd}" Padding="14">
-
+
+
+
-
-
-
-
-
-
-
-
-
+ (three in CSA), and the third box only appears when the type calls for it. Crew names
+ use the ad-hoc "whoever is entering data right now" persona, not the Stammdaten roster
+ persona (#137). -->
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/LageBuch.App.Shared/Views/TaskDialogView.axaml b/src/LageBuch.App.Shared/Views/TaskDialogView.axaml
index ba53ce3..ea3079a 100644
--- a/src/LageBuch.App.Shared/Views/TaskDialogView.axaml
+++ b/src/LageBuch.App.Shared/Views/TaskDialogView.axaml
@@ -1,17 +1,10 @@
-
-
-
-
-
+
+
-
+
-
-
+
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
-
+
diff --git a/src/LageBuch.App.Shared/Views/TasksView.axaml b/src/LageBuch.App.Shared/Views/TasksView.axaml
index b0baae6..483ce2e 100644
--- a/src/LageBuch.App.Shared/Views/TasksView.axaml
+++ b/src/LageBuch.App.Shared/Views/TasksView.axaml
@@ -1,6 +1,7 @@
@@ -19,16 +20,10 @@
-
-
+
@@ -44,8 +39,7 @@
Background="#0F141D" BorderBrush="{StaticResource HairlineBrush}" BorderThickness="1"
CornerRadius="{StaticResource RadiusMd}" Padding="14">
-
-
+
@@ -54,9 +48,8 @@
-
-
-
+
+
@@ -65,33 +58,31 @@
-
-
-
-
+
+
-
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
+
+
diff --git a/src/LageBuch.Persistence/MasterData/MasterDataSet.cs b/src/LageBuch.Persistence/MasterData/MasterDataSet.cs
index 294f97e..998f968 100644
--- a/src/LageBuch.Persistence/MasterData/MasterDataSet.cs
+++ b/src/LageBuch.Persistence/MasterData/MasterDataSet.cs
@@ -70,6 +70,106 @@ public sealed record Person(string LastName, string FirstName, string? Role, str
///
public sealed record Vehicle(string Wache, string CallSign, int Seats);
+///
+/// The single source of truth for fictional example data shown in input-field placeholders
+/// (#137) and used to build test/screenshot fixtures. Real personnel and call-sign data must
+/// never be compiled into the app (see the privacy note on ), so every value
+/// here is invented, following the classic German "Mustermann/Musterfrau" placeholder-name
+/// convention already used ad hoc across the test suite. Centralizing it means a placeholder and
+/// the fixture that renders it for a PR screenshot can never drift apart.
+///
+public static class AnonymizedExampleData
+{
+ // Roster (Stammdaten) persona.
+ public const string PersonLastName = "Mustermann";
+ public const string PersonFirstName = "Max";
+ public const string PersonLastNameAlt = "Musterfrau";
+ public const string PersonFirstNameAlt = "Erika";
+ public const string PhoneNumber = "01 71 / 1 23 45 67";
+ public const string PhoneNumberAlt = "01 71 / 7 65 43 21";
+
+ // Ad-hoc persona: whoever is entering data right now (operator, Truppführer/-mann).
+ // Deliberately a different fictional surname from the roster persona above, so a screenshot
+ // never shows the same invented person as both "the roster entry" and "today's operator".
+ public const string OperatorSurname = "Müller";
+ public const string OperatorSurnameAlt = "Schmidt";
+ public const string OperatorSurnameThird = "Wagner";
+ // First name for the full-name example (OperatorPromptView's NAME field). Deliberately not
+ // "Thomas" — that would read as a real contributor's actual name rather than a placeholder.
+ public const string OperatorFirstName = "Jens";
+
+ // Callsigns / brigades.
+ public const string CallSign = "FFB 1/40/1";
+ public const string SecondCallSign = "FFB 1/44/1";
+ public const string OtherBrigadeCallSign = "Aich 42/1";
+ public const string Brigade = "FFB Wache 1";
+ public const string SecondBrigade = "Aich";
+
+ // Misc categorical examples used by non-Kräfte views.
+ public const string BuildingName = "Haus A";
+ public const string FileName = "Lageplan.pdf";
+ public const string RoleExample = "GF";
+ public const string SectionExample = "Abschnitt 1";
+ public const string TimerMinutesExample = "30";
+ public const string LinkName = "Wetterdienst";
+ public const string LinkUrl = "https://dwd.de";
+
+ // Derived placeholder strings. Compile-time const concatenation, so the "z. B." prefix and
+ // the underlying value can never drift apart from one another.
+ public const string CallSignPlaceholder = "z. B. " + CallSign;
+ public const string SecondCallSignPlaceholder = "z. B. " + SecondCallSign;
+ public const string BrigadePlaceholder = "z. B. " + Brigade;
+ public const string PersonLastNamePlaceholder = "z. B. " + PersonLastName;
+ public const string PersonFirstNamePlaceholder = "z. B. " + PersonFirstName;
+ public const string PersonDisplayNamePlaceholder = "z. B. " + PersonLastName + ", " + PersonFirstName;
+ public const string PhoneNumberPlaceholder = "z. B. " + PhoneNumber;
+ public const string OperatorNamePlaceholder = "z. B. " + OperatorSurname;
+ public const string OperatorNamePlaceholderAlt = "z. B. " + OperatorSurnameAlt;
+ public const string OperatorNamePlaceholderThird = "z. B. " + OperatorSurnameThird;
+ // Full-name form, for the one field that asks for a proper name rather than a short crew/
+ // assignee entry (OperatorPromptView's NAME field).
+ public const string OperatorFullNamePlaceholder = "z. B. " + OperatorSurname + ", " + OperatorFirstName;
+ public const string BuildingNamePlaceholder = "z. B. " + BuildingName;
+ public const string FileNamePlaceholder = "z. B. " + FileName;
+ public const string RolePlaceholder = "z. B. " + RoleExample;
+ public const string SectionPlaceholder = "z. B. " + SectionExample;
+ public const string TimerMinutesPlaceholder = "z. B. " + TimerMinutesExample;
+ public const string LinkNamePlaceholder = "z. B. " + LinkName;
+ public const string LinkUrlPlaceholder = "z. B. " + LinkUrl;
+
+ // A field that is genuinely optional reuses this idiom rather than inventing a second
+ // convention for the same idea (see OperatorPromptView's KeywordBox).
+ public const string OptionalCallSignPlaceholder = "optional, z. B. " + CallSign;
+
+ // Ready-built collections for fixtures that need a fuller MasterDataSet (render/PR-screenshot
+ // tests). Built from the same constants above so a single-value placeholder and a list-based
+ // fixture never show contradictory example data.
+ public static readonly IReadOnlyList Brigades =
+ new[] { Brigade, "FFB Wache 2", SecondBrigade, "Puch", "Emmering" };
+
+ public static readonly IReadOnlyList RadioCallSigns =
+ new[] { CallSign, "FFB 1/23/1", OtherBrigadeCallSign, "Land 1" };
+
+ public static readonly IReadOnlyList Vehicles = new[]
+ {
+ new Vehicle(Brigade, CallSign, 9),
+ new Vehicle(Brigade, SecondCallSign, 6),
+ new Vehicle(SecondBrigade, OtherBrigadeCallSign, 6),
+ };
+
+ public static readonly IReadOnlyList Personnel = new[]
+ {
+ new Person(PersonLastName, PersonFirstName, "ZF", "Land 1", PhoneNumber),
+ new Person(PersonLastNameAlt, PersonFirstNameAlt, "GF", null, PhoneNumberAlt),
+ };
+
+ public static readonly IReadOnlyList Links = new[]
+ {
+ new Link(LinkName, LinkUrl),
+ new Link("Kartendienst", "https://example.org/karte"),
+ };
+}
+
public sealed record MasterDataSet(
IReadOnlyList Roles,
IReadOnlyList Status,
diff --git a/tests/LageBuch.Acceptance.Tests/EtbTabRenderTests.cs b/tests/LageBuch.Acceptance.Tests/EtbTabRenderTests.cs
new file mode 100644
index 0000000..104b657
--- /dev/null
+++ b/tests/LageBuch.Acceptance.Tests/EtbTabRenderTests.cs
@@ -0,0 +1,61 @@
+using Avalonia.Controls;
+using Avalonia.Headless;
+using Avalonia.Headless.XUnit;
+using Avalonia.Threading;
+using LageBuch.App.Shared.Views;
+using LageBuch.AppLogic;
+using LageBuch.AppLogic.Services;
+using LageBuch.AppLogic.ViewModels;
+using LageBuch.Domain;
+using LageBuch.Persistence.MasterData;
+
+namespace LageBuch.Acceptance.Tests;
+
+// #137: labeled-field pass over EINSATZTAGEBUCH (previously had no render-test coverage at all).
+// Doubles as the PR screenshot capture, same idiom as ForcesTabRenderTests.
+public class EtbTabRenderTests
+{
+ private static (Window Window, IncidentWorkspaceViewModel Vm) ShowWorkspace()
+ {
+ var session = LocalIncidentSession.StartNew(new FakeStore(), new FixedClock(),
+ new SessionOperator(AnonymizedExampleData.OperatorSurname, "FFB 12/1"), "/x.fwincident",
+ Array.Empty<(string, bool)>(), Array.Empty<(string, bool)>());
+ var vm = new IncidentWorkspaceViewModel(session, new FixedClock(), new NoopTicker(),
+ MasterData(), new FakeDialogs(), new NoopAlarmService(), new NoopIncidentHostController());
+ var window = new Window { Content = new IncidentWorkspaceView { DataContext = vm }, Width = 1920, Height = 1032 };
+ window.Show();
+ Dispatcher.UIThread.RunJobs();
+ return (window, vm);
+ }
+
+ private static MasterDataSet MasterData() => MasterDataSet.Empty with
+ {
+ RadioCallSigns = AnonymizedExampleData.RadioCallSigns,
+ };
+
+ private static TabControl Tabs(Window window) =>
+ ((IncidentWorkspaceView)window.Content!).GetControl("ModuleTabs");
+
+ [AvaloniaFact]
+ public void Etb_tab_shows_an_added_entry()
+ {
+ var (window, vm) = ShowWorkspace();
+ var tabs = Tabs(window);
+ tabs.SelectedIndex = 1; // ETB
+ Dispatcher.UIThread.RunJobs();
+ Assert.Equal("ETB", ((TabItem)tabs.SelectedItem!).Header);
+
+ vm.Etb.NewFrom = AnonymizedExampleData.CallSign;
+ vm.Etb.NewTo = AnonymizedExampleData.SecondCallSign;
+ vm.Etb.NewText = "Lagemeldung übermittelt";
+ vm.Etb.AddEntryCommand.Execute(null);
+ Dispatcher.UIThread.RunJobs();
+
+ Assert.Single(vm.Etb.Entries, e => e.Text == "Lagemeldung übermittelt");
+
+ var dir = Path.Combine(Path.GetTempPath(), "lagebuch-shots");
+ Directory.CreateDirectory(dir);
+ using var frame = window.CaptureRenderedFrame()!;
+ frame.SavePng(Path.Combine(dir, "etb-entry.png"));
+ }
+}
diff --git a/tests/LageBuch.Acceptance.Tests/ForcesTabRenderTests.cs b/tests/LageBuch.Acceptance.Tests/ForcesTabRenderTests.cs
index c7e6697..7aa2ec2 100644
--- a/tests/LageBuch.Acceptance.Tests/ForcesTabRenderTests.cs
+++ b/tests/LageBuch.Acceptance.Tests/ForcesTabRenderTests.cs
@@ -19,7 +19,7 @@ public class ForcesTabRenderTests
private static (Window Window, IncidentWorkspaceViewModel Vm) ShowWorkspace()
{
var session = LocalIncidentSession.StartNew(new FakeStore(), new FixedClock(),
- new SessionOperator("Müller", "FFB 12/1"), "/x.fwincident",
+ new SessionOperator(AnonymizedExampleData.OperatorSurname, "FFB 12/1"), "/x.fwincident",
Array.Empty<(string, bool)>(), Array.Empty<(string, bool)>());
var vm = new IncidentWorkspaceViewModel(session, new FixedClock(), new NoopTicker(),
MasterData(), new FakeDialogs(), new NoopAlarmService(), new NoopIncidentHostController());
@@ -31,15 +31,14 @@ private static (Window Window, IncidentWorkspaceViewModel Vm) ShowWorkspace()
private static MasterDataSet MasterData() => MasterDataSet.Empty with
{
- Brigades = new[] { "FFB Wache 1", "Aich" },
+ Brigades = new[] { AnonymizedExampleData.Brigade, AnonymizedExampleData.SecondBrigade },
UnitStatus = new[] { "Alarmiert", "Auf Anfahrt", "Im Einsatz" },
- RadioCallSigns = new[] { "FFB 1/40/1", "FFB 1/44/1", "Aich 42/1" },
- Vehicles = new[]
+ RadioCallSigns = new[]
{
- new Vehicle("FFB Wache 1", "FFB 1/40/1", 9),
- new Vehicle("FFB Wache 1", "FFB 1/44/1", 6),
- new Vehicle("Aich", "Aich 42/1", 6),
+ AnonymizedExampleData.CallSign, AnonymizedExampleData.SecondCallSign,
+ AnonymizedExampleData.OtherBrigadeCallSign,
},
+ Vehicles = AnonymizedExampleData.Vehicles,
};
private static TabControl Tabs(Window window) =>
@@ -57,7 +56,7 @@ private static void Capture(Window window, string name)
public void Vehicle_selection_presets_the_dock_and_the_row_shows_1_1_2_strength()
{
var (window, vm) = ShowWorkspace();
- Tabs(window).SelectedIndex = 2; // KRÄFTE
+ Tabs(window).SelectedIndex = 4; // KRÄFTE
Dispatcher.UIThread.RunJobs();
vm.Forces.NewBrigade = "FFB Wache 1";
@@ -84,7 +83,7 @@ public void Vehicle_selection_presets_the_dock_and_the_row_shows_1_1_2_strength(
public void A_strength_correction_is_committed_as_one_edit_and_exposes_the_verlauf()
{
var (window, vm) = ShowWorkspace();
- Tabs(window).SelectedIndex = 2;
+ Tabs(window).SelectedIndex = 4; // KRÄFTE
vm.Forces.NewBrigade = "Aich";
vm.Forces.NewMannschaftCount = 6;
vm.Forces.AddForceCommand.Execute(null);
@@ -110,7 +109,7 @@ public void A_strength_correction_is_committed_as_one_edit_and_exposes_the_verla
public void A_row_can_be_removed_completely_and_the_etb_records_it()
{
var (window, vm) = ShowWorkspace();
- Tabs(window).SelectedIndex = 2;
+ Tabs(window).SelectedIndex = 4; // KRÄFTE
vm.Forces.NewBrigade = "FFB Wache 1";
vm.Forces.NewMannschaftCount = 6;
vm.Forces.AddForceCommand.Execute(null);
@@ -143,7 +142,7 @@ public void A_taken_vehicle_is_hidden_from_the_dropdown_and_a_typed_duplicate_is
// ViewLocator content does not materialize under the headless host, so the view is built
// against the same ForcesViewModel instance instead.
var session = LocalIncidentSession.StartNew(new FakeStore(), new FixedClock(),
- new SessionOperator("Müller", "FFB 12/1"), "/x.fwincident",
+ new SessionOperator(AnonymizedExampleData.OperatorSurname, "FFB 12/1"), "/x.fwincident",
Array.Empty<(string, bool)>(), Array.Empty<(string, bool)>());
var vm = new IncidentWorkspaceViewModel(session, new FixedClock(), new NoopTicker(),
MasterData(), new FakeDialogs(), new NoopAlarmService(), new NoopIncidentHostController());
diff --git a/tests/LageBuch.Acceptance.Tests/MasterDataEditorRenderTests.cs b/tests/LageBuch.Acceptance.Tests/MasterDataEditorRenderTests.cs
index 609d19d..0b2f435 100644
--- a/tests/LageBuch.Acceptance.Tests/MasterDataEditorRenderTests.cs
+++ b/tests/LageBuch.Acceptance.Tests/MasterDataEditorRenderTests.cs
@@ -142,12 +142,14 @@ public void Fahrzeuge_section_renders_wache_callsign_and_seats_per_row()
var boxes = view.GetVisualDescendants().OfType().ToList();
// PlaceholderText: Watermark is obsolete in C# under Avalonia 12 (XAML keeps the old name).
- Assert.Contains(boxes, b => b.Text == "FFB Wache 1" && b.PlaceholderText == "Wache");
- Assert.Contains(boxes, b => b.Text == "FFB 1/44/1" && b.PlaceholderText == "Funkrufname");
+ // #137: placeholders now show anonymized examples instead of restating the label.
+ Assert.Contains(boxes, b => b.Text == "FFB Wache 1" && b.PlaceholderText == AnonymizedExampleData.BrigadePlaceholder);
+ Assert.Contains(boxes, b => b.Text == "FFB 1/44/1" && b.PlaceholderText == AnonymizedExampleData.CallSignPlaceholder);
// The suggestions come from the master data lists; free text stays possible.
- var wacheBox = boxes.Single(b => b.PlaceholderText == "Wache");
+ var wacheBox = boxes.Single(b => b.PlaceholderText == AnonymizedExampleData.BrigadePlaceholder);
Assert.Equal(new[] { "FFB Wache 1", "Aich", "Puch" }, wacheBox.ItemsSource);
- Assert.Equal(new[] { "FFB 1/10/1", "Aich 42/1", "Land 1" }, boxes.Single(b => b.PlaceholderText == "Funkrufname").ItemsSource);
+ Assert.Equal(new[] { "FFB 1/10/1", "Aich 42/1", "Land 1" },
+ boxes.Single(b => b.PlaceholderText == AnonymizedExampleData.CallSignPlaceholder).ItemsSource);
Assert.Contains(view.GetVisualDescendants().OfType(), n => n.Value == 9);
Assert.Equal(new[] { new Vehicle("FFB Wache 1", "FFB 1/44/1", 9) }, section.ToValues());
diff --git a/tests/LageBuch.Acceptance.Tests/RolesTabRenderTests.cs b/tests/LageBuch.Acceptance.Tests/RolesTabRenderTests.cs
new file mode 100644
index 0000000..defdbb0
--- /dev/null
+++ b/tests/LageBuch.Acceptance.Tests/RolesTabRenderTests.cs
@@ -0,0 +1,63 @@
+using Avalonia.Controls;
+using Avalonia.Headless;
+using Avalonia.Headless.XUnit;
+using Avalonia.Threading;
+using LageBuch.App.Shared.Views;
+using LageBuch.AppLogic;
+using LageBuch.AppLogic.Services;
+using LageBuch.AppLogic.ViewModels;
+using LageBuch.Domain;
+using LageBuch.Persistence.MasterData;
+
+namespace LageBuch.Acceptance.Tests;
+
+// #137: labeled-field pass over FUNKTIONEN. Doubles as the PR screenshot capture, same idiom
+// as ForcesTabRenderTests.
+public class RolesTabRenderTests
+{
+ private static (Window Window, IncidentWorkspaceViewModel Vm) ShowWorkspace()
+ {
+ var session = LocalIncidentSession.StartNew(new FakeStore(), new FixedClock(),
+ new SessionOperator(AnonymizedExampleData.OperatorSurname, "FFB 12/1"), "/x.fwincident",
+ Array.Empty<(string, bool)>(), Array.Empty<(string, bool)>());
+ var vm = new IncidentWorkspaceViewModel(session, new FixedClock(), new NoopTicker(),
+ MasterData(), new FakeDialogs(), new NoopAlarmService(), new NoopIncidentHostController());
+ var window = new Window { Content = new IncidentWorkspaceView { DataContext = vm }, Width = 1920, Height = 1032 };
+ window.Show();
+ Dispatcher.UIThread.RunJobs();
+ return (window, vm);
+ }
+
+ private static MasterDataSet MasterData() => MasterDataSet.Empty with
+ {
+ Roles = new[] { AnonymizedExampleData.RoleExample, "ZF" },
+ RadioCallSigns = AnonymizedExampleData.RadioCallSigns,
+ };
+
+ private static TabControl Tabs(Window window) =>
+ ((IncidentWorkspaceView)window.Content!).GetControl("ModuleTabs");
+
+ [AvaloniaFact]
+ public void Funktionen_tab_shows_a_running_assignment()
+ {
+ var (window, vm) = ShowWorkspace();
+ var tabs = Tabs(window);
+ tabs.SelectedIndex = 3; // FUNKTIONEN
+ Dispatcher.UIThread.RunJobs();
+ Assert.Equal("FUNKTIONEN", ((TabItem)tabs.SelectedItem!).Header);
+
+ vm.Roles.NewRole = AnonymizedExampleData.RoleExample;
+ vm.Roles.NewPersonName = $"{AnonymizedExampleData.PersonLastName}, {AnonymizedExampleData.PersonFirstName}";
+ vm.Roles.NewSection = AnonymizedExampleData.SectionExample;
+ vm.Roles.NewPhone = AnonymizedExampleData.PhoneNumber;
+ vm.Roles.AddRoleCommand.Execute(null);
+ Dispatcher.UIThread.RunJobs();
+
+ Assert.Single(vm.Roles.Roles);
+
+ var dir = Path.Combine(Path.GetTempPath(), "lagebuch-shots");
+ Directory.CreateDirectory(dir);
+ using var frame = window.CaptureRenderedFrame()!;
+ frame.SavePng(Path.Combine(dir, "roles-assignment.png"));
+ }
+}
diff --git a/tests/LageBuch.Acceptance.Tests/TaskDialogRenderTests.cs b/tests/LageBuch.Acceptance.Tests/TaskDialogRenderTests.cs
new file mode 100644
index 0000000..7a707a6
--- /dev/null
+++ b/tests/LageBuch.Acceptance.Tests/TaskDialogRenderTests.cs
@@ -0,0 +1,38 @@
+using Avalonia.Controls;
+using Avalonia.Headless;
+using Avalonia.Headless.XUnit;
+using Avalonia.Threading;
+using LageBuch.App.Shared.Views;
+using LageBuch.AppLogic;
+using LageBuch.AppLogic.ViewModels;
+using LageBuch.Domain;
+using LageBuch.Persistence.MasterData;
+
+namespace LageBuch.Acceptance.Tests;
+
+// #137: labeled-field pass over the "Task erstellen" dialog (previously had no render-test
+// coverage at all). Hosted directly, same idiom as ForcesTabRenderTests' strength-editor test.
+public class TaskDialogRenderTests
+{
+ [AvaloniaFact]
+ public void Task_dialog_renders_with_prefilled_text()
+ {
+ var session = LocalIncidentSession.StartNew(new FakeStore(), new FixedClock(),
+ new SessionOperator(AnonymizedExampleData.OperatorSurname, "FFB 12/1"), "/x.fwincident",
+ Array.Empty<(string, bool)>(), Array.Empty<(string, bool)>());
+ var masterData = MasterDataSet.Empty with { RadioCallSigns = AnonymizedExampleData.RadioCallSigns };
+ var dialogVm = new TaskDialogViewModel(session, masterData, "Lagemeldung übermittelt", () => { });
+
+ var view = new TaskDialogView { DataContext = dialogVm };
+ var window = new Window { Content = view, Width = 560, Height = 360 };
+ window.Show();
+ Dispatcher.UIThread.RunJobs();
+
+ Assert.Equal("Lagemeldung übermittelt", dialogVm.Text);
+
+ var dir = Path.Combine(Path.GetTempPath(), "lagebuch-shots");
+ Directory.CreateDirectory(dir);
+ using var frame = window.CaptureRenderedFrame()!;
+ frame.SavePng(Path.Combine(dir, "task-dialog.png"));
+ }
+}
diff --git a/tests/LageBuch.Acceptance.Tests/WorkspaceRenderHelper.cs b/tests/LageBuch.Acceptance.Tests/WorkspaceRenderHelper.cs
index a38870b..14ecc41 100644
--- a/tests/LageBuch.Acceptance.Tests/WorkspaceRenderHelper.cs
+++ b/tests/LageBuch.Acceptance.Tests/WorkspaceRenderHelper.cs
@@ -35,16 +35,13 @@ private static MasterDataSet Md() => MasterDataSet.Empty with
new ChecklistTemplateItem("Fahrzeug abgerüstet und einsatzbereit?", true),
},
TruppTypes = new[] { "Angriffstrupp", "Sicherheitstrupp", "CSA-Trupp" },
- Brigades = new[] { "FFB Wache 1", "FFB Wache 2", "Aich", "Puch", "Emmering" },
+ Brigades = AnonymizedExampleData.Brigades,
UnitStatus = new[] { "Alarmiert", "Auf Anfahrt", "Bereitstellungsraum", "Im Einsatz" },
- RadioCallSigns = new[] { "FFB 1/40/1", "FFB 1/23/1", "Aich 42/1", "Land 1" },
- Links = new[] { new Link("Wetterdienst", "https://dwd.de"), new Link("Kartendienst", "https://example.org/karte") },
- // Fictional roster: the real personnel.json is gitignored, so tests supply their own.
- Personnel = new[]
- {
- new Person("Mustermann", "Max", "ZF", "Land 1", "01 71 / 1 23 45 67"),
- new Person("Musterfrau", "Erika", "GF", null, "01 71 / 7 65 43 21"),
- },
+ RadioCallSigns = AnonymizedExampleData.RadioCallSigns,
+ Links = AnonymizedExampleData.Links,
+ // Fictional roster: the real personnel.json is gitignored, so tests supply their own
+ // (#137: same anonymized fixture the app's own placeholders are built from).
+ Personnel = AnonymizedExampleData.Personnel,
};
public static IncidentWorkspaceViewModel BuildEditableWorkspaceWithAllBars(
@@ -63,7 +60,8 @@ public static IncidentWorkspaceViewModel BuildEditableWorkspaceWithAllBars(
// Keyword + Einsatznummer both set: the common post-#69 shape once ILS has called back --
// Stichwort as the header hero, the Einsatznummer as the secondary chip beside it.
var session = LocalIncidentSession.StartNew(new FakeStore(), clock,
- new SessionOperator("Müller", "FFB 12/1"), "/x.fwincident", checklistAufbau, checklistAbbau,
+ new SessionOperator(AnonymizedExampleData.OperatorSurname, "FFB 12/1"), "/x.fwincident",
+ checklistAufbau, checklistAbbau,
new IncidentNumber("B 1.2 260715 123"), keyword: "B3P");
var ticker = new ManualTicker();
var vm = new IncidentWorkspaceViewModel(session, clock, ticker, Md(),
@@ -73,8 +71,8 @@ public static IncidentWorkspaceViewModel BuildEditableWorkspaceWithAllBars(
// 1) ILS reminder running, 2) SCBA pressure-control due, 3) Rückzugsalarm active.
// The ILS reminder auto-starts with the incident (no manual start).
vm.Scba.NewDesignation = "Angriffstrupp";
- vm.Scba.NewTruppfuehrer = "Müller";
- vm.Scba.NewTruppmann = "Schmidt";
+ vm.Scba.NewTruppfuehrer = AnonymizedExampleData.OperatorSurname;
+ vm.Scba.NewTruppmann = AnonymizedExampleData.OperatorSurnameAlt;
vm.Scba.AddTruppCommand.Execute(null);
var row = vm.Scba.Trupps[^1];
row.PressureInput = 300;
diff --git a/tests/LageBuch.AppLogic.Tests/ForcesViewModelTests.cs b/tests/LageBuch.AppLogic.Tests/ForcesViewModelTests.cs
index a950a07..e214dc6 100644
--- a/tests/LageBuch.AppLogic.Tests/ForcesViewModelTests.cs
+++ b/tests/LageBuch.AppLogic.Tests/ForcesViewModelTests.cs
@@ -10,15 +10,10 @@ public class ForcesViewModelTests
private static MasterDataSet Md() => MasterDataSet.Empty with
{
- RadioCallSigns = new[] { "FFB 1/40/1" },
- Brigades = new[] { "FFB Wache 1", "Aich" },
+ RadioCallSigns = new[] { AnonymizedExampleData.CallSign },
+ Brigades = new[] { AnonymizedExampleData.Brigade, AnonymizedExampleData.SecondBrigade },
UnitStatus = new[] { "Alarmiert", "Im Einsatz" },
- Vehicles = new[]
- {
- new Vehicle("FFB Wache 1", "FFB 1/40/1", 9),
- new Vehicle("FFB Wache 1", "FFB 1/44/1", 6),
- new Vehicle("Aich", "Aich 42/1", 6),
- },
+ Vehicles = AnonymizedExampleData.Vehicles,
};
[Fact]