diff --git a/docs/gui-bedienanleitung.md b/docs/gui-bedienanleitung.md index 40b71c9..97ac6b8 100644 --- a/docs/gui-bedienanleitung.md +++ b/docs/gui-bedienanleitung.md @@ -112,6 +112,17 @@ Per Rechtsklick steht zusätzlich ein Kontextmenü zur Verfügung: | **Meldung kopieren** | Kopiert alle markierten Zeilen in die Zwischenablage (nur aktiv, wenn eine Auswahl besteht) | | **Alle Meldungen kopieren** | Kopiert alle aktuell angezeigten Meldungen in die Zwischenablage | +#### Meldungen leeren + +Unterhalb des Meldungsbereichs befindet sich der Button **„Meldungen leeren"**. +Ein Klick darauf entfernt alle aktuell angezeigten Meldungen sofort und +vollständig. + +Beim Laden einer neuen oder bestehenden Konfiguration über **„Neu"** oder +**„Öffnen"** wird der Meldungsbereich automatisch geleert, sodass keine +Meldungen aus einer früheren Sitzung oder einer vorher geladenen Datei sichtbar +bleiben. + --- ## 4. Aktionen diff --git a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java index c8a742f..64bd509 100644 --- a/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java +++ b/pdf-umbenenner-adapter-in-gui/src/main/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiConfigurationEditorWorkspace.java @@ -150,6 +150,12 @@ public final class GuiConfigurationEditorWorkspace { */ final Button technicalTestsButton = new Button("Technische Tests ausführen"); + /** + * The "Meldungen leeren" button below the central message area. + * Package-private to allow state assertions in smoke tests. + */ + final Button clearMessagesButton = new Button("Meldungen leeren"); + private static final Path DEFAULT_SAVE_PATH = Paths.get("config/application.properties"); private final GuiConfigurationFileLoader configurationFileLoader; @@ -896,6 +902,7 @@ public final class GuiConfigurationEditorWorkspace { this.welcomeGuidanceVisible = false; statusLabel.setVisible(false); statusLabel.setManaged(false); + pendingMessages.clear(); refreshView(); runEditorValidation(); } @@ -1560,6 +1567,11 @@ public final class GuiConfigurationEditorWorkspace { card.getChildren().add(messagesListView); + clearMessagesButton.setOnAction(e -> clearMessages()); + HBox clearButtonRow = new HBox(clearMessagesButton); + clearButtonRow.setAlignment(Pos.CENTER_RIGHT); + card.getChildren().add(clearButtonRow); + // Populate immediately so the area is not blank before the first validation run. refreshMessagesArea(); return card; @@ -1832,6 +1844,17 @@ public final class GuiConfigurationEditorWorkspace { } } + /** + * Removes all entries from the central message area. + *
+ * Clears {@link #pendingMessages} and refreshes the visible message area. + * Must be called on the JavaFX Application Thread. + */ + void clearMessages() { + pendingMessages.clear(); + refreshMessagesArea(); + } + /** * Formats a message timestamp as {@code [HH:mm:ss]} in the system default zone. * diff --git a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiMessageAreaSmokeTest.java b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiMessageAreaSmokeTest.java index 5feb0a3..933376b 100644 --- a/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiMessageAreaSmokeTest.java +++ b/pdf-umbenenner-adapter-in-gui/src/test/java/de/gecheckt/pdf/umbenenner/adapter/in/gui/GuiMessageAreaSmokeTest.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.io.TempDir; import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationEditorStateFactory; import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationFileSnapshot; +import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiMessageEntry; import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiMessageSeverity; import de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily; import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.EffectiveApiKeyDescriptor; @@ -609,6 +610,76 @@ class GuiMessageAreaSmokeTest { }); } + // ========================================================================= + // Scenario: message area is cleared when a new configuration is applied + // ========================================================================= + + /** + * Smoke test: after "Neu" is triggered on a workspace that already has a message, the + * central message area must no longer contain that pre-existing message entry. + *
+ * This verifies that {@code applyEditorState} clears {@code pendingMessages} so that + * messages from a previous configuration do not bleed into the freshly loaded one. + */ + @Test + void newConfiguration_clearsPreviousMessages() throws Exception { + runOnFx(() -> { + GuiConfigurationEditorWorkspace ws = new GuiConfigurationEditorWorkspace(Optional.empty()); + // Seed a message that must not survive a "Neu" action. + ws.pendingMessages.add( + GuiMessageEntry.of(GuiMessageSeverity.ERROR, "Alter Fehler", "Test")); + ws.refreshMessagesArea(); + assertFalse(ws.pendingMessages.isEmpty(), + "Pre-condition: pending messages must not be empty before 'Neu'"); + + ws.requestNewConfiguration(); + + assertFalse( + ws.pendingMessages.stream() + .anyMatch(m -> "Alter Fehler".equals(m.text())), + "The pre-existing message must have been removed after 'Neu'"); + }); + } + + // ========================================================================= + // Scenario: "Meldungen leeren" button clears the message area + // ========================================================================= + + /** + * Smoke test: invoking {@code clearMessages()} removes all entries from + * {@code pendingMessages} and causes the visible message area to show the + * placeholder instead of previous entries. + */ + @Test + void clearMessages_removesAllEntries() throws Exception { + runOnFx(() -> { + GuiConfigurationEditorWorkspace ws = new GuiConfigurationEditorWorkspace(Optional.empty()); + ws.requestNewConfiguration(); + + // Seed some messages. + ws.pendingMessages.add( + GuiMessageEntry.of(GuiMessageSeverity.INFO, "Info-Meldung", "Test")); + ws.pendingMessages.add( + GuiMessageEntry.of(GuiMessageSeverity.WARNING, "Warnung", "Test")); + ws.refreshMessagesArea(); + assertFalse(ws.pendingMessages.isEmpty(), + "Pre-condition: pending messages must not be empty before clearing"); + + ws.clearMessages(); + + assertTrue(ws.pendingMessages.isEmpty(), + "pendingMessages must be empty after clearMessages()"); + assertTrue(ws.messagesAreaBox.getChildren().isEmpty() + || ws.messagesAreaBox.getChildren().stream() + .noneMatch(n -> n instanceof javafx.scene.text.TextFlow tf + && tf.getChildren().stream() + .anyMatch(c -> c instanceof javafx.scene.text.Text t + && (t.getText().contains("Info-Meldung") + || t.getText().contains("Warnung")))), + "messagesAreaBox must not contain the cleared message texts after clearMessages()"); + }); + } + // ========================================================================= // Scenario: ai.provider.active field-error label is registered and shown // =========================================================================