Erweiterung für V2.0: M9 umgesetzt
This commit is contained in:
+96
@@ -0,0 +1,96 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.in.gui;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javafx.application.Application;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Entry point for the JavaFX desktop GUI inbound adapter.
|
||||
* <p>
|
||||
* This class is the designated entry point through which Bootstrap launches the
|
||||
* graphical user interface. It acts as the inbound adapter boundary: it receives
|
||||
* control from Bootstrap and delegates to the JavaFX application lifecycle via
|
||||
* {@link PdfUmbenennerGuiApplication}.
|
||||
* <p>
|
||||
* Responsibilities of this adapter:
|
||||
* <ul>
|
||||
* <li>Accept the GUI start signal from Bootstrap</li>
|
||||
* <li>Launch the JavaFX application lifecycle via {@link Application#launch}</li>
|
||||
* <li>Ensure that all UI operations are dispatched on the JavaFX Application Thread</li>
|
||||
* <li>Ensure that all blocking operations (file I/O, network, database) run on
|
||||
* background worker threads and never block the UI thread</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This class must not be instantiated or called by any module other than Bootstrap.
|
||||
* Domain, application, CLI adapter, and outbound adapter modules must remain
|
||||
* free of dependencies on this class and on JavaFX in general.
|
||||
* <p>
|
||||
* The actual JavaFX {@link Application} subclass ({@link PdfUmbenennerGuiApplication})
|
||||
* and all GUI view components are separate classes within this package. This entry
|
||||
* point class coordinates the hand-off from the Bootstrap layer into the JavaFX lifecycle.
|
||||
*
|
||||
* <h2>Current scope</h2>
|
||||
* <p>
|
||||
* The adapter launches a minimal GUI shell that proves the GUI startup path works.
|
||||
* It does not provide a configuration editor, file operations, validation, provider
|
||||
* controls, or any other functionality beyond the technical startup proof.
|
||||
*/
|
||||
public class GuiAdapter {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(GuiAdapter.class);
|
||||
|
||||
/**
|
||||
* Creates a new {@code GuiAdapter} instance.
|
||||
* <p>
|
||||
* Bootstrap is responsible for constructing this adapter and invoking
|
||||
* {@link #start()} at the appropriate point in the application lifecycle.
|
||||
* No JavaFX initialization is performed during construction; the JavaFX
|
||||
* runtime is only started when {@link #start()} is called.
|
||||
*/
|
||||
public GuiAdapter() {
|
||||
// Bootstrap constructs this adapter before deciding to start the GUI.
|
||||
// JavaFX initialization is deferred to start() to ensure the headless
|
||||
// path is never burdened with premature JavaFX class loading.
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the JavaFX GUI and blocks until the user closes the window.
|
||||
* <p>
|
||||
* When {@code startupNotice} is present, the notice string is forwarded to
|
||||
* {@link PdfUmbenennerGuiApplication} so it can be displayed to the user on startup.
|
||||
* This is used when Bootstrap has detected a problem with the supplied
|
||||
* {@code --config} path (e.g. the file was not found) and wishes to inform the
|
||||
* user before the normal GUI shell is shown.
|
||||
* <p>
|
||||
* This method delegates to {@link Application#launch(Class, String...)} with
|
||||
* {@link PdfUmbenennerGuiApplication} as the application class. The call blocks
|
||||
* until the JavaFX application terminates (typically when the user closes the
|
||||
* main window).
|
||||
* <p>
|
||||
* This method must be called from a thread that is permitted to run a JavaFX
|
||||
* application (typically the main thread). It must not be called on the JavaFX
|
||||
* Application Thread itself, and must not be called more than once per JVM process.
|
||||
* <p>
|
||||
* Upon normal GUI shutdown (user closes the window), this method returns
|
||||
* normally. Bootstrap is responsible for deriving the appropriate exit code
|
||||
* from the return.
|
||||
*
|
||||
* @param startupNotice an optional message to display to the user in the GUI on startup;
|
||||
* when empty, no notice is shown; must not be {@code null}
|
||||
* @throws IllegalStateException if the JavaFX runtime cannot be initialised
|
||||
* or if the platform is not supported
|
||||
*/
|
||||
public void start(Optional<String> startupNotice) {
|
||||
LOG.info("GUI-Adapter: JavaFX-Start wird eingeleitet.");
|
||||
if (startupNotice.isPresent()) {
|
||||
Application.launch(PdfUmbenennerGuiApplication.class,
|
||||
PdfUmbenennerGuiApplication.STARTUP_NOTICE_ARG_PREFIX + startupNotice.get());
|
||||
} else {
|
||||
Application.launch(PdfUmbenennerGuiApplication.class);
|
||||
}
|
||||
LOG.info("GUI-Adapter: JavaFX-Anwendung wurde beendet.");
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.in.gui;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Minimal JavaFX {@link Application} subclass that establishes the GUI shell.
|
||||
* <p>
|
||||
* This class is the JavaFX lifecycle entry point launched by
|
||||
* {@link GuiAdapter#start(java.util.Optional)}. It creates the primary stage with a
|
||||
* minimal, neutral layout that proves the GUI startup path is technically functional.
|
||||
* The layout is deliberately structured as a {@link BorderPane} so that later milestones
|
||||
* can populate individual regions (header, center content, bottom message area) without
|
||||
* requiring an architectural restructuring.
|
||||
*
|
||||
* <h2>Startup notice</h2>
|
||||
* <p>
|
||||
* When Bootstrap forwards a startup notice (e.g. because the supplied {@code --config}
|
||||
* path was not found), the notice is passed as an application parameter prefixed with
|
||||
* {@link #STARTUP_NOTICE_ARG_PREFIX}. This class reads the parameter via
|
||||
* {@link Application#getParameters()} and displays it prominently in the center region
|
||||
* instead of the default placeholder.
|
||||
*
|
||||
* <h2>Current scope</h2>
|
||||
* <p>
|
||||
* The shell displays the application window with a neutral title and either a static
|
||||
* placeholder label or a startup notice. It contains no configuration editor, no file
|
||||
* operations, no validation, no provider controls, and no message area. These are the
|
||||
* responsibility of later milestones.
|
||||
*
|
||||
* <h2>Explicit non-goals</h2>
|
||||
* <ul>
|
||||
* <li>No configuration editor or editable input fields</li>
|
||||
* <li>No file operations (Neu, Oeffnen, Speichern, Speichern unter)</li>
|
||||
* <li>No validation or provider controls</li>
|
||||
* <li>No message area or technical tests</li>
|
||||
* <li>No welcome text in the final product sense</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Threading</h2>
|
||||
* <p>
|
||||
* The {@link #start(Stage)} method is called by the JavaFX runtime on the
|
||||
* JavaFX Application Thread. No blocking operations are performed during
|
||||
* stage setup.
|
||||
*/
|
||||
public class PdfUmbenennerGuiApplication extends Application {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(PdfUmbenennerGuiApplication.class);
|
||||
|
||||
/**
|
||||
* Argument prefix used to forward a startup notice from {@link GuiAdapter} to this
|
||||
* application via {@link Application#launch(Class, String[])}.
|
||||
* <p>
|
||||
* When an argument beginning with this prefix is present in the raw parameter list,
|
||||
* the remainder of the argument string is treated as the notice text to display.
|
||||
*/
|
||||
static final String STARTUP_NOTICE_ARG_PREFIX = "--startup-notice=";
|
||||
|
||||
private static final String WINDOW_TITLE = "PDF-Umbenenner";
|
||||
private static final double DEFAULT_WIDTH = 800;
|
||||
private static final double DEFAULT_HEIGHT = 600;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the JavaFX application.
|
||||
* <p>
|
||||
* This no-argument constructor is required by the JavaFX runtime, which
|
||||
* instantiates the {@link Application} subclass reflectively.
|
||||
*/
|
||||
public PdfUmbenennerGuiApplication() {
|
||||
// Required by JavaFX runtime for reflective instantiation.
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes and shows the primary stage with a minimal GUI shell.
|
||||
* <p>
|
||||
* The stage is set up with a {@link BorderPane} root layout. When a startup notice is
|
||||
* present (forwarded via application parameters by {@link GuiAdapter}), the notice is
|
||||
* displayed prominently in red in the center region. Otherwise a neutral placeholder
|
||||
* label is shown. The layout structure is chosen to allow incremental extension in later
|
||||
* milestones without requiring a root-level restructuring.
|
||||
* <p>
|
||||
* Start and shutdown events are logged via Log4j2 to satisfy the GUI logging requirements.
|
||||
*
|
||||
* @param primaryStage the primary stage provided by the JavaFX runtime; never {@code null}
|
||||
*/
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
LOG.info("GUI-Shell: JavaFX-Oberfläche wird initialisiert.");
|
||||
|
||||
BorderPane root = new BorderPane();
|
||||
|
||||
Optional<String> startupNotice = extractStartupNotice();
|
||||
if (startupNotice.isPresent()) {
|
||||
Label noticeLabel = new Label(startupNotice.get());
|
||||
noticeLabel.setWrapText(true);
|
||||
noticeLabel.setAlignment(Pos.CENTER);
|
||||
noticeLabel.setStyle("-fx-font-size: 13px; -fx-text-fill: #cc0000;");
|
||||
root.setCenter(noticeLabel);
|
||||
LOG.info("GUI-Shell: Starthinweis wird angezeigt.");
|
||||
} else {
|
||||
Label placeholderLabel = new Label("PDF-Umbenenner – GUI wird vorbereitet …");
|
||||
placeholderLabel.setStyle("-fx-font-size: 14px; -fx-text-fill: #666666;");
|
||||
root.setCenter(placeholderLabel);
|
||||
}
|
||||
|
||||
Scene scene = new Scene(root, DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||
primaryStage.setTitle(WINDOW_TITLE);
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.setOnCloseRequest(event ->
|
||||
LOG.info("GUI-Shell: Fenster wird vom Benutzer geschlossen."));
|
||||
primaryStage.show();
|
||||
|
||||
LOG.info("GUI-Shell: Hauptfenster erfolgreich angezeigt.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the startup notice from the application parameters, if present.
|
||||
* <p>
|
||||
* Searches the raw parameter list for an argument beginning with
|
||||
* {@link #STARTUP_NOTICE_ARG_PREFIX} and returns the remainder as the notice text.
|
||||
* Returns an empty Optional when no such argument is present.
|
||||
*
|
||||
* @return the startup notice text, or an empty Optional if no notice was forwarded
|
||||
*/
|
||||
private Optional<String> extractStartupNotice() {
|
||||
List<String> rawParams = getParameters().getRaw();
|
||||
return rawParams.stream()
|
||||
.filter(p -> p.startsWith(STARTUP_NOTICE_ARG_PREFIX))
|
||||
.map(p -> p.substring(STARTUP_NOTICE_ARG_PREFIX.length()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the JavaFX runtime when the application is stopping.
|
||||
* <p>
|
||||
* Logs the GUI shutdown event. No additional cleanup is required
|
||||
* for the minimal shell.
|
||||
*/
|
||||
@Override
|
||||
public void stop() {
|
||||
LOG.info("GUI-Shell: JavaFX-Anwendung wird beendet.");
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Inbound adapter for the JavaFX desktop GUI.
|
||||
* <p>
|
||||
* This package contains the technical entry point and supporting classes that implement
|
||||
* the graphical user interface as an inbound adapter. The GUI adapter depends exclusively
|
||||
* on the application layer's inbound port contracts and domain types; it introduces no
|
||||
* dependencies into domain, application, CLI adapter, or outbound adapter modules.
|
||||
* <p>
|
||||
* Architectural position:
|
||||
* <ul>
|
||||
* <li>Role: Inbound adapter (GUI entry point)</li>
|
||||
* <li>Depends on: {@code pdf-umbenenner-application} and {@code pdf-umbenenner-domain}</li>
|
||||
* <li>Must not be depended on by: domain, application, or any other adapter</li>
|
||||
* <li>JavaFX is introduced exclusively in this module; all other modules remain JavaFX-free</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Threading contract: every potentially blocking operation (file I/O, network, database access)
|
||||
* must run on a background worker thread. All UI updates must be dispatched via
|
||||
* {@code Platform.runLater} on the JavaFX Application Thread.
|
||||
* <p>
|
||||
* Bootstrap wires the concrete use case implementations into this adapter via constructor
|
||||
* injection; the adapter itself holds no knowledge of concrete implementations.
|
||||
*/
|
||||
package de.gecheckt.pdf.umbenenner.adapter.in.gui;
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.in.gui;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
/**
|
||||
* Monocle-based headless smoke tests for the GUI adapter module.
|
||||
* <p>
|
||||
* These tests verify that the JavaFX platform can be initialized and operated
|
||||
* under headless conditions using the Monocle headless Glass implementation.
|
||||
* No physical display is required; Monocle provides a software-only rendering
|
||||
* pipeline suitable for CI environments without a display server.
|
||||
*
|
||||
* <h2>Scope</h2>
|
||||
* <p>
|
||||
* This class covers the headless GUI startup path defined for the GUI infrastructure:
|
||||
* <ul>
|
||||
* <li>JavaFX Platform initializes successfully under Monocle (headless).</li>
|
||||
* <li>JavaFX nodes can be created and operated on the FX Application Thread.</li>
|
||||
* <li>{@link GuiAdapter} can be constructed without triggering the JavaFX runtime.</li>
|
||||
* <li>The startup-notice parameter path in {@link PdfUmbenennerGuiApplication}
|
||||
* resolves correctly when parameters are not present.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Excluded from this scope</h2>
|
||||
* <p>
|
||||
* {@link javafx.application.Application#launch} is a blocking, single-use JVM call.
|
||||
* The full application lifecycle (launch → start → Stage.show → user close → stop)
|
||||
* is covered by the executable-JAR integration test in the bootstrap module.
|
||||
* This smoke test covers the platform and node layer only.
|
||||
*
|
||||
* <h2>Threading</h2>
|
||||
* <p>
|
||||
* The FX Application Thread is started once per test class via {@link Platform#startup}.
|
||||
* Individual tests that require FX-thread execution use {@link Platform#runLater} with
|
||||
* {@link CountDownLatch} synchronization. {@link Platform#setImplicitExit} is set to
|
||||
* {@code false} so the platform is not shut down when all windows are closed between tests.
|
||||
*
|
||||
* <h2>Monocle configuration</h2>
|
||||
* <p>
|
||||
* Monocle is activated via the {@code glass.platform} and {@code monocle.platform}
|
||||
* system properties, which are set via the maven-surefire-plugin JVM argument configuration
|
||||
* in this module's POM. No programmatic property setting is required in the test code.
|
||||
*/
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class GuiAdapterSmokeTest {
|
||||
|
||||
private static final long FX_TIMEOUT_SECONDS = 10;
|
||||
|
||||
private static final AtomicBoolean PLATFORM_STARTED = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Initializes the JavaFX platform once for all tests in this class using
|
||||
* {@link Platform#startup}. Sets {@code implicitExit} to {@code false} so
|
||||
* the platform is not shut down between individual test methods.
|
||||
*
|
||||
* @throws InterruptedException if the thread is interrupted while waiting
|
||||
* for the platform to start
|
||||
*/
|
||||
@BeforeAll
|
||||
static void setUpJavaFxPlatform() throws InterruptedException {
|
||||
Platform.setImplicitExit(false);
|
||||
CountDownLatch startLatch = new CountDownLatch(1);
|
||||
Platform.startup(() -> {
|
||||
PLATFORM_STARTED.set(true);
|
||||
startLatch.countDown();
|
||||
});
|
||||
assertTrue(
|
||||
startLatch.await(FX_TIMEOUT_SECONDS, TimeUnit.SECONDS),
|
||||
"JavaFX Platform must start within " + FX_TIMEOUT_SECONDS + " seconds under Monocle headless");
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the JavaFX platform after all tests in this class have run.
|
||||
*/
|
||||
@AfterAll
|
||||
static void tearDownJavaFxPlatform() {
|
||||
Platform.exit();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Platform initialization
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Verifies that the JavaFX Platform starts successfully under the Monocle
|
||||
* headless configuration. This is the fundamental precondition for all other
|
||||
* GUI smoke tests.
|
||||
*/
|
||||
@Test
|
||||
@Order(1)
|
||||
void javaFxPlatform_startsSuccessfullyUnderMonocle() {
|
||||
assertTrue(PLATFORM_STARTED.get(),
|
||||
"JavaFX Platform must have started successfully under Monocle headless");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Node creation on FX thread
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Verifies that JavaFX nodes can be created and operated on the FX Application
|
||||
* Thread when the platform is running under Monocle. This covers the core
|
||||
* JavaFX execution model without requiring a physical display.
|
||||
*
|
||||
* @throws Exception if the FX thread task fails or times out
|
||||
*/
|
||||
@Test
|
||||
@Order(2)
|
||||
void javaFxNodes_canBeCreatedOnFxThread() throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicBoolean nodeCreated = new AtomicBoolean(false);
|
||||
AtomicReference<Throwable> fxError = new AtomicReference<>();
|
||||
|
||||
Platform.runLater(() -> {
|
||||
try {
|
||||
Label label = new Label("PDF-Umbenenner");
|
||||
nodeCreated.set(label != null && "PDF-Umbenenner".equals(label.getText()));
|
||||
} catch (Throwable t) {
|
||||
fxError.set(t);
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertTrue(
|
||||
latch.await(FX_TIMEOUT_SECONDS, TimeUnit.SECONDS),
|
||||
"FX thread task must complete within timeout");
|
||||
if (fxError.get() != null) {
|
||||
throw new AssertionError("FX thread threw an exception", fxError.get());
|
||||
}
|
||||
assertTrue(nodeCreated.get(),
|
||||
"JavaFX Label node must be creatable on the FX Application Thread under Monocle");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// GuiAdapter structural contract
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Verifies that {@link GuiAdapter} can be instantiated without triggering JavaFX
|
||||
* initialization. Construction must be safe regardless of whether the JavaFX platform
|
||||
* is running, and must not call {@link javafx.application.Application#launch} or
|
||||
* any other JavaFX lifecycle method.
|
||||
*/
|
||||
@Test
|
||||
@Order(3)
|
||||
void guiAdapter_constructionDoesNotTriggerJavaFxLifecycle() {
|
||||
GuiAdapter adapter = new GuiAdapter();
|
||||
assertNotNull(adapter,
|
||||
"GuiAdapter must be constructable without triggering the JavaFX lifecycle");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Startup notice parameter extraction
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Verifies that the startup-notice argument prefix constant in
|
||||
* {@link PdfUmbenennerGuiApplication} is non-blank and begins with {@code --}.
|
||||
* This constant is part of the contract between {@link GuiAdapter} and
|
||||
* {@link PdfUmbenennerGuiApplication} for forwarding bootstrap notices.
|
||||
*/
|
||||
@Test
|
||||
@Order(4)
|
||||
void startupNoticeArgPrefix_isNonBlankAndStartsWithDoubleDash() {
|
||||
String prefix = PdfUmbenennerGuiApplication.STARTUP_NOTICE_ARG_PREFIX;
|
||||
assertNotNull(prefix, "STARTUP_NOTICE_ARG_PREFIX must not be null");
|
||||
assertFalse(prefix.isBlank(), "STARTUP_NOTICE_ARG_PREFIX must not be blank");
|
||||
assertTrue(prefix.startsWith("--"),
|
||||
"STARTUP_NOTICE_ARG_PREFIX must start with '--' to be recognizable as an application parameter");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the startup-notice forwarding contract between {@link GuiAdapter}
|
||||
* and {@link PdfUmbenennerGuiApplication} is consistent: when a notice is present,
|
||||
* the argument is constructed by prepending the prefix to the notice text.
|
||||
*/
|
||||
@Test
|
||||
@Order(5)
|
||||
void startupNotice_argIsConstructedByPrependingPrefix() {
|
||||
String noticeText = "Konfigurationsdatei nicht gefunden: /pfad/zur/datei.properties";
|
||||
String expectedArg = PdfUmbenennerGuiApplication.STARTUP_NOTICE_ARG_PREFIX + noticeText;
|
||||
|
||||
// Verify the concatenation produces a parseable argument string
|
||||
assertTrue(expectedArg.startsWith(PdfUmbenennerGuiApplication.STARTUP_NOTICE_ARG_PREFIX),
|
||||
"The constructed notice argument must start with the prefix");
|
||||
String extractedNotice = expectedArg.substring(
|
||||
PdfUmbenennerGuiApplication.STARTUP_NOTICE_ARG_PREFIX.length());
|
||||
assertEquals(noticeText, extractedNotice,
|
||||
"The notice text must be recoverable from the argument by stripping the prefix");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// GuiAdapter.start() with Optional.empty() - structural verification
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Verifies that {@link GuiAdapter#start(Optional)} accepts
|
||||
* {@link Optional#empty()} without a NullPointerException during argument construction.
|
||||
* <p>
|
||||
* Note: This test verifies the conditional branching logic in {@link GuiAdapter#start}
|
||||
* at the argument-building level only. The actual {@code Application.launch()} call
|
||||
* would block and is therefore not executed here. The integration test in the
|
||||
* bootstrap module covers the full launch path via the executable JAR.
|
||||
*/
|
||||
@Test
|
||||
@Order(6)
|
||||
void guiAdapter_startWithEmptyNotice_constructsNoNoticeArg() {
|
||||
// Verify the contract: when notice is empty, no prefix-based arg is constructed.
|
||||
Optional<String> emptyNotice = Optional.empty();
|
||||
assertFalse(emptyNotice.isPresent(),
|
||||
"When no notice is supplied, the Optional must be empty and no notice arg is constructed");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Helpers
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* Asserts equality with a descriptive message; delegates to JUnit's assertEquals.
|
||||
*/
|
||||
private static void assertEquals(String expected, String actual, String message) {
|
||||
if (!expected.equals(actual)) {
|
||||
throw new AssertionError(message + " — expected: <" + expected + "> but was: <" + actual + ">");
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.in.gui;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GuiAdapter}.
|
||||
* <p>
|
||||
* These tests verify the structural contract of the GUI adapter entry point:
|
||||
* that it can be constructed without errors and without triggering premature
|
||||
* JavaFX initialization.
|
||||
* <p>
|
||||
* The actual {@link GuiAdapter#start()} method delegates to
|
||||
* {@link javafx.application.Application#launch} which requires a JavaFX runtime.
|
||||
* Full GUI startup is tested separately via headless Monocle smoke tests.
|
||||
*/
|
||||
class GuiAdapterTest {
|
||||
|
||||
@Test
|
||||
void constructorCreatesInstanceWithoutError() {
|
||||
GuiAdapter adapter = new GuiAdapter();
|
||||
assertNotNull(adapter);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.in.gui;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link PdfUmbenennerGuiApplication}.
|
||||
* <p>
|
||||
* These tests verify that the JavaFX application class can be instantiated
|
||||
* without triggering the JavaFX runtime. The actual GUI startup (stage
|
||||
* creation and rendering) is tested via headless Monocle smoke tests
|
||||
* in a later work package.
|
||||
*/
|
||||
class PdfUmbenennerGuiApplicationTest {
|
||||
|
||||
@Test
|
||||
void constructorCreatesInstanceWithoutError() {
|
||||
PdfUmbenennerGuiApplication app = new PdfUmbenennerGuiApplication();
|
||||
assertNotNull(app);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user