From 96e51ac95a0608528988f7b53a40e3d335c020a6 Mon Sep 17 00:00:00 2001 From: yoep Date: Sun, 21 Apr 2024 22:28:25 +0200 Subject: [PATCH 1/4] Improved code with java 11+ features & added some additional tests --- .../boot/javafx/font/FontRegistryImpl.java | 13 ++-- .../javafx/font/controls/AbstractIcon.java | 6 +- .../javafx/stage/BorderlessStageWrapper.java | 9 ++- .../boot/javafx/text/LocaleTextImpl.java | 4 +- .../spring/boot/javafx/view/ViewLoader.java | 4 +- .../boot/javafx/view/ViewLoaderImpl.java | 77 +++++++++---------- .../boot/javafx/view/ViewManagerImpl.java | 8 +- .../spring/boot/javafx/TestConfiguration.java | 4 + .../javafx/controllers/MyTestController.java | 17 ++++ .../javafx/font/controls/IconRegularTest.java | 50 ++++++++++++ .../javafx/font/controls/IconSolidTest.java | 29 +++++++ .../boot/javafx/font/controls/IconTest.java | 71 +++++++++++++++++ .../javafx/stage/BorderlessStageTest.java | 55 +++++++++++++ .../stage/BorderlessStageWrapperTest.java | 2 +- .../boot/javafx/view/ViewLoaderImplTest.java | 59 ++++++++++++++ .../views/load_view_with_controller.fxml | 6 ++ .../views/load_view_without_controller.fxml | 6 ++ 17 files changed, 355 insertions(+), 65 deletions(-) create mode 100644 src/test/java/com/github/spring/boot/javafx/controllers/MyTestController.java create mode 100644 src/test/java/com/github/spring/boot/javafx/font/controls/IconRegularTest.java create mode 100644 src/test/java/com/github/spring/boot/javafx/font/controls/IconSolidTest.java create mode 100644 src/test/java/com/github/spring/boot/javafx/font/controls/IconTest.java create mode 100644 src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageTest.java create mode 100644 src/test/java/com/github/spring/boot/javafx/view/ViewLoaderImplTest.java create mode 100644 src/test/resources/views/load_view_with_controller.fxml create mode 100644 src/test/resources/views/load_view_without_controller.fxml diff --git a/src/main/java/com/github/spring/boot/javafx/font/FontRegistryImpl.java b/src/main/java/com/github/spring/boot/javafx/font/FontRegistryImpl.java index 2ba894f..23dc451 100644 --- a/src/main/java/com/github/spring/boot/javafx/font/FontRegistryImpl.java +++ b/src/main/java/com/github/spring/boot/javafx/font/FontRegistryImpl.java @@ -1,11 +1,10 @@ package com.github.spring.boot.javafx.font; import javafx.scene.text.Font; -import org.springframework.util.Assert; -import java.net.URL; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; /** @@ -29,15 +28,15 @@ public static FontRegistryImpl getInstance() { @Override public Font loadFont(String filename) { - Assert.notNull(filename, "filename cannot be null"); - Font defaultFont = Font.getDefault(); + Objects.requireNonNull(filename, "filename cannot be null"); + var defaultFont = Font.getDefault(); return loadFont(filename, defaultFont.getSize()); } @Override public Font loadFont(String filename, double size) { - Assert.notNull(filename, "filename cannot be null"); + Objects.requireNonNull(filename, "filename cannot be null"); if (loadedFonts.containsKey(filename)) return createFontFromAlreadyLoadedFont(loadedFonts.get(filename), size); @@ -46,8 +45,8 @@ public Font loadFont(String filename, double size) { } private Font loadFontResource(String filename, double size) { - URL resource = getClass().getResource(FONT_DIRECTORY + filename); - Font font = Optional.ofNullable(Font.loadFont(resource.toExternalForm(), size)) + var resource = getClass().getResource(FONT_DIRECTORY + filename); + var font = Optional.ofNullable(Font.loadFont(resource.toExternalForm(), size)) .orElseThrow(() -> new FontException(filename)); loadedFonts.put(filename, font); diff --git a/src/main/java/com/github/spring/boot/javafx/font/controls/AbstractIcon.java b/src/main/java/com/github/spring/boot/javafx/font/controls/AbstractIcon.java index 7d53366..d0e99ce 100644 --- a/src/main/java/com/github/spring/boot/javafx/font/controls/AbstractIcon.java +++ b/src/main/java/com/github/spring/boot/javafx/font/controls/AbstractIcon.java @@ -67,7 +67,7 @@ private void init(String filename) { } private void initializeFont(String filename) { - Font font = FontRegistryImpl.getInstance().loadFont(filename); + var font = FontRegistryImpl.getInstance().loadFont(filename); fontFamily = font.getFamily(); setFont(font); @@ -76,8 +76,8 @@ private void initializeFont(String filename) { private void initializeSizeFactor() { sizeFactorProperty.addListener((observable, oldValue, newValue) -> { updating = true; - Font oldFont = getFont(); - double fontSize = getActualSize(newValue.doubleValue(), oldFont.getSize()); + var oldFont = getFont(); + var fontSize = getActualSize(newValue.doubleValue(), oldFont.getSize()); setFont(Font.font(fontFamily, FontWeight.findByName(oldFont.getStyle()), fontSize)); updating = false; diff --git a/src/main/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapper.java b/src/main/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapper.java index 48d0472..8a6dec3 100644 --- a/src/main/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapper.java +++ b/src/main/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapper.java @@ -10,7 +10,8 @@ import javafx.stage.StageStyle; import lombok.Getter; import org.springframework.lang.Nullable; -import org.springframework.util.Assert; + +import java.util.Objects; /** * The {@link BorderlessStageWrapper} wraps around an existing stage and converts it into a borderless stage. @@ -41,7 +42,7 @@ public class BorderlessStageWrapper { //region Constructors public BorderlessStageWrapper(Stage stage) { - Assert.notNull(stage, "stage cannot be null"); + Objects.requireNonNull(stage, "stage cannot be null"); this.stage = stage; init(); } @@ -140,14 +141,14 @@ private void initializeListeners() { } private void removeSceneListeners(Scene scene) { - Assert.notNull(scene, "scene cannot be null"); + Objects.requireNonNull(scene, "scene cannot be null"); scene.removeEventHandler(MouseEvent.MOUSE_MOVED, mouseMovedEventHandler); scene.removeEventHandler(MouseEvent.MOUSE_PRESSED, mousePressedEventHandler); scene.removeEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDraggedEventHandler); } private void addSceneListeners(Scene scene) { - Assert.notNull(scene, "scene cannot be null"); + Objects.requireNonNull(scene, "scene cannot be null"); scene.addEventHandler(MouseEvent.MOUSE_MOVED, mouseMovedEventHandler); scene.addEventHandler(MouseEvent.MOUSE_PRESSED, mousePressedEventHandler); scene.addEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDraggedEventHandler); diff --git a/src/main/java/com/github/spring/boot/javafx/text/LocaleTextImpl.java b/src/main/java/com/github/spring/boot/javafx/text/LocaleTextImpl.java index 493b0c6..ae104ee 100644 --- a/src/main/java/com/github/spring/boot/javafx/text/LocaleTextImpl.java +++ b/src/main/java/com/github/spring/boot/javafx/text/LocaleTextImpl.java @@ -9,9 +9,9 @@ import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.MessageSourceResourceBundle; import org.springframework.context.support.ResourceBundleMessageSource; -import org.springframework.util.Assert; import java.util.Locale; +import java.util.Objects; import java.util.ResourceBundle; /** @@ -30,7 +30,7 @@ public class LocaleTextImpl implements LocaleText { * @param messageSource set the message source to use. */ public LocaleTextImpl(ResourceBundleMessageSource messageSource) { - Assert.notNull(messageSource, "messageSource cannot be null"); + Objects.requireNonNull(messageSource, "messageSource cannot be null"); this.messageSource = messageSource; init(); diff --git a/src/main/java/com/github/spring/boot/javafx/view/ViewLoader.java b/src/main/java/com/github/spring/boot/javafx/view/ViewLoader.java index 476eab8..c8abed9 100644 --- a/src/main/java/com/github/spring/boot/javafx/view/ViewLoader.java +++ b/src/main/java/com/github/spring/boot/javafx/view/ViewLoader.java @@ -8,11 +8,11 @@ public interface ViewLoader { /** * The directory containing the FXML files. */ - String VIEW_DIRECTORY = "views"; + String VIEW_DIRECTORY = "/views"; /** * The directory contain */ - String IMAGE_DIRECTORY = "images"; + String IMAGE_DIRECTORY = "/images"; /** * Set the UI scale of the views. diff --git a/src/main/java/com/github/spring/boot/javafx/view/ViewLoaderImpl.java b/src/main/java/com/github/spring/boot/javafx/view/ViewLoaderImpl.java index d0217f7..598a034 100644 --- a/src/main/java/com/github/spring/boot/javafx/view/ViewLoaderImpl.java +++ b/src/main/java/com/github/spring/boot/javafx/view/ViewLoaderImpl.java @@ -6,7 +6,6 @@ import com.github.spring.boot.javafx.ui.stage.StageAware; import javafx.application.Platform; import javafx.fxml.FXMLLoader; -import javafx.geometry.Rectangle2D; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; @@ -16,8 +15,6 @@ import javafx.stage.Modality; import javafx.stage.Screen; import javafx.stage.Stage; -import lombok.AllArgsConstructor; -import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.context.ApplicationContext; @@ -47,6 +44,9 @@ public class ViewLoaderImpl implements ViewLoader { * @param localeText Set the UI text manager. */ public ViewLoaderImpl(ApplicationContext applicationContext, ViewManager viewManager, LocaleText localeText) { + Objects.requireNonNull(applicationContext, "applicationContext cannot be null"); + Objects.requireNonNull(viewManager, "viewManager cannot be null"); + Objects.requireNonNull(localeText, "localeText cannot be null"); this.applicationContext = applicationContext; this.viewManager = viewManager; this.localeText = localeText; @@ -68,7 +68,7 @@ public void setScale(float scale) { @Override public void show(String view, ViewProperties properties) { Assert.hasText(view, "view cannot be empty"); - Assert.notNull(properties, "properties cannot be null"); + Objects.requireNonNull(properties, "properties cannot be null"); Stage stage = viewManager.getPrimaryStage() .orElseThrow(StageNotFoundException::new); @@ -77,24 +77,24 @@ public void show(String view, ViewProperties properties) { @Override public void show(Stage window, String view, ViewProperties properties) { - Assert.notNull(window, "window cannot be empty"); + Objects.requireNonNull(window, "window cannot be empty"); Assert.hasText(view, "view cannot be empty"); - Assert.notNull(properties, "properties cannot be null"); + Objects.requireNonNull(properties, "properties cannot be null"); showScene(window, view, properties); } @Override public void showWindow(String view, ViewProperties properties) { Assert.hasText(view, "view cannot be empty"); - Assert.notNull(properties, "properties cannot be null"); + Objects.requireNonNull(properties, "properties cannot be null"); Platform.runLater(() -> showScene(new Stage(), view, properties)); } @Override public void showWindow(Pane pane, Object controller, ViewProperties properties) { - Assert.notNull(pane, "pane cannot be null"); - Assert.notNull(controller, "controller cannot be null"); - Assert.notNull(properties, "properties cannot be null"); + Objects.requireNonNull(pane, "pane cannot be null"); + Objects.requireNonNull(controller, "controller cannot be null"); + Objects.requireNonNull(properties, "properties cannot be null"); Platform.runLater(() -> showScene(new Stage(), new SceneInfo(new Scene(pane), pane, controller), properties)); } @@ -102,7 +102,7 @@ public void showWindow(Pane pane, Object controller, ViewProperties properties) @Override public T load(String view) { Assert.hasText(view, "view cannot be empty"); - FXMLLoader loader = loadResource(view); + var loader = loadResource(view); loader.setControllerFactory(applicationContext::getBean); return loadComponent(loader); @@ -111,8 +111,8 @@ public T load(String view) { @Override public T load(String view, Object controller) { Assert.hasText(view, "view cannot be empty"); - Assert.notNull(controller, "controller cannot be null"); - FXMLLoader loader = loadResource(view); + Objects.requireNonNull(controller, "controller cannot be null"); + var loader = loadResource(view); loader.setController(controller); return loadComponent(loader); @@ -131,7 +131,7 @@ public T load(String view, Object controller) { * @return Returns the loaded view component on success, else null when the loading failed. */ protected T loadComponent(FXMLLoader loader) { - Assert.notNull(loader, "loader cannot be null"); + Objects.requireNonNull(loader, "loader cannot be null"); loader.setResources(localeText.getResourceBundle()); try { @@ -150,8 +150,8 @@ protected T loadComponent(FXMLLoader loader) { * @return Returns the loader for the given view file. */ protected FXMLLoader loadResource(String view) { - Assert.notNull(view, "view cannot be null"); - ClassPathResource componentResource = new ClassPathResource(ViewLoader.VIEW_DIRECTORY + File.separator + view); + Objects.requireNonNull(view, "view cannot be null"); + var componentResource = new ClassPathResource(ViewLoader.VIEW_DIRECTORY + File.separator + view); if (!componentResource.exists()) throw new ViewNotFoundException(view); @@ -165,7 +165,7 @@ protected FXMLLoader loadResource(String view) { private SceneInfo loadView(String view, ViewProperties properties) throws ViewNotFoundException { Assert.hasText(view, "view cannot be empty"); - ClassPathResource fxmlResourceFile = new ClassPathResource(ViewLoader.VIEW_DIRECTORY + File.separator + view); + var fxmlResourceFile = new ClassPathResource(ViewLoader.VIEW_DIRECTORY + File.separator + view); if (fxmlResourceFile.exists()) { FXMLLoader loader; @@ -207,7 +207,7 @@ private SceneInfo loadView(String view, ViewProperties properties) throws ViewNo } private void showScene(Stage window, String view, ViewProperties properties) { - SceneInfo sceneInfo = loadView(view, properties); + var sceneInfo = loadView(view, properties); if (sceneInfo != null) { showScene(window, sceneInfo, properties); @@ -217,8 +217,8 @@ private void showScene(Stage window, String view, ViewProperties properties) { } private void showScene(Stage window, SceneInfo sceneInfo, ViewProperties properties) { - Scene scene = sceneInfo.getScene(); - Object controller = sceneInfo.getController(); + var scene = sceneInfo.scene(); + var controller = sceneInfo.controller(); window.setScene(scene); viewManager.addWindowView(window, scene); @@ -266,7 +266,7 @@ private void setWindowViewProperties(Stage window, ViewProperties properties) { * @param window Set the window to center. */ private void centerOnScreen(Stage window) { - Rectangle2D screenBounds = Screen.getPrimary().getVisualBounds(); + var screenBounds = Screen.getPrimary().getVisualBounds(); window.setX((screenBounds.getWidth() - window.getWidth()) / 2); window.setY((screenBounds.getHeight() - window.getHeight()) / 2); @@ -274,7 +274,7 @@ private void centerOnScreen(Stage window) { private Image loadWindowIcon(String iconName) { try { - ClassPathResource iconResource = new ClassPathResource(ViewLoader.IMAGE_DIRECTORY + File.separator + iconName); + var iconResource = new ClassPathResource(ViewLoader.IMAGE_DIRECTORY + File.separator + iconName); if (iconResource.exists()) { return new Image(iconResource.getInputStream()); @@ -287,13 +287,13 @@ private Image loadWindowIcon(String iconName) { } private void initWindowScale(SceneInfo sceneInfo) { - ScaleAware controller = (ScaleAware) sceneInfo.getController(); + var controller = (ScaleAware) sceneInfo.controller(); - controller.scale(sceneInfo.getScene(), sceneInfo.getRoot(), scale); + controller.scale(sceneInfo.scene(), sceneInfo.root(), scale); } private void initWindowSize(Scene scene, SizeAware controller) { - Stage window = (Stage) scene.getWindow(); + var window = (Stage) scene.getWindow(); controller.setInitialSize(window); window.widthProperty().addListener((observable, oldValue, newValue) -> { if (window.isShowing()) { @@ -313,14 +313,14 @@ private void initWindowSize(Scene scene, SizeAware controller) { } private void initWindowEvents(Scene scene, StageAware controller) { - final Stage window = (Stage) scene.getWindow(); + final var window = (Stage) scene.getWindow(); window.setOnShown(event -> controller.onShown(window)); window.setOnCloseRequest(event -> controller.onClosed(window)); } private void onScaleChanged(final float newValue) { - for (ScaleAware scaleAware : applicationContext.getBeansOfType(ScaleAware.class).values()) { + for (var scaleAware : applicationContext.getBeansOfType(ScaleAware.class).values()) { try { scaleAware.onScaleChanged(newValue); } catch (Exception ex) { @@ -331,20 +331,13 @@ private void onScaleChanged(final float newValue) { //endregion - @Getter - @AllArgsConstructor - static class SceneInfo { - /** - * The scene for the loaded FXML file. - */ - private Scene scene; - /** - * The root region of the loaded FXML file. - */ - private Region root; - /** - * The FXML controller that has been created. - */ - private Object controller; + /** + * Contains the general information about a certain scene which might actively be rendered within JavaFX. + * + * @param scene The scene to be rendered. + * @param root The root region of the scene. + * @param controller The controller of the scene. + */ + record SceneInfo(Scene scene, Region root, Object controller) { } } diff --git a/src/main/java/com/github/spring/boot/javafx/view/ViewManagerImpl.java b/src/main/java/com/github/spring/boot/javafx/view/ViewManagerImpl.java index 50ab4e2..e7a1684 100644 --- a/src/main/java/com/github/spring/boot/javafx/view/ViewManagerImpl.java +++ b/src/main/java/com/github/spring/boot/javafx/view/ViewManagerImpl.java @@ -13,10 +13,10 @@ import lombok.Value; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.util.Assert; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; @Slf4j @@ -84,7 +84,7 @@ public int getTotalWindows() { @Override public void registerPrimaryStage(Stage primaryStage) { - Assert.notNull(primaryStage, "primaryStage cannot be null"); + Objects.requireNonNull(primaryStage, "primaryStage cannot be null"); if (getPrimaryStage().isPresent()) { log.warn("Ignoring primary stage register as one has already been registered"); return; @@ -96,8 +96,8 @@ public void registerPrimaryStage(Stage primaryStage) { @Override public void addWindowView(Stage window, Scene view) { - Assert.notNull(window, "window cannot be null"); - Assert.notNull(view, "view cannot be null"); + Objects.requireNonNull(window, "window cannot be null"); + Objects.requireNonNull(view, "view cannot be null"); addWindowView(window, view, false); } diff --git a/src/test/java/com/github/spring/boot/javafx/TestConfiguration.java b/src/test/java/com/github/spring/boot/javafx/TestConfiguration.java index eb0b2b0..4ad1730 100644 --- a/src/test/java/com/github/spring/boot/javafx/TestConfiguration.java +++ b/src/test/java/com/github/spring/boot/javafx/TestConfiguration.java @@ -1,10 +1,14 @@ package com.github.spring.boot.javafx; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ResourceBundleMessageSource; @Configuration +@ComponentScan({ + "com.github.spring.boot.javafx.controllers" +}) public class TestConfiguration { @Bean public ResourceBundleMessageSource resourceBundleMessageSource() { diff --git a/src/test/java/com/github/spring/boot/javafx/controllers/MyTestController.java b/src/test/java/com/github/spring/boot/javafx/controllers/MyTestController.java new file mode 100644 index 0000000..d04c024 --- /dev/null +++ b/src/test/java/com/github/spring/boot/javafx/controllers/MyTestController.java @@ -0,0 +1,17 @@ +package com.github.spring.boot.javafx.controllers; + +import com.github.spring.boot.javafx.stereotype.ViewController; +import javafx.fxml.FXML; +import javafx.scene.layout.Pane; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@ToString +@EqualsAndHashCode +@ViewController +public class MyTestController { + @FXML + Pane root; +} diff --git a/src/test/java/com/github/spring/boot/javafx/font/controls/IconRegularTest.java b/src/test/java/com/github/spring/boot/javafx/font/controls/IconRegularTest.java new file mode 100644 index 0000000..7487211 --- /dev/null +++ b/src/test/java/com/github/spring/boot/javafx/font/controls/IconRegularTest.java @@ -0,0 +1,50 @@ +package com.github.spring.boot.javafx.font.controls; + +import javafx.geometry.Insets; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testfx.framework.junit5.ApplicationExtension; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; + +@ExtendWith(ApplicationExtension.class) +class IconRegularTest { + + @Test + void testNewInstance() { + var result = new IconRegular(); + + assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); + } + + @Test + void testNewInstanceWithUnicode() { + var unicode = IconRegular.ANGRY_UNICODE; + + var result = new IconRegular(unicode); + + assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); + assertEquals(unicode, result.getText()); + } + + @Test + void testNewInstanceWithBuilder() { + var unicode = IconRegular.ANGRY_UNICODE; + var padding = new Insets(12, 20, 30, 40); + + var result = IconRegular.builder() + .unicode(unicode) + .padding(padding) + .visible(false) + .styleClasses(asList("foo", "bar")) + .build(); + + assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); + assertEquals(unicode, result.getText()); + assertEquals(padding, result.getPadding()); + assertFalse(result.isVisible()); + assertTrue(result.getStyleClass().contains("foo")); + assertTrue(result.getStyleClass().contains("bar")); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/spring/boot/javafx/font/controls/IconSolidTest.java b/src/test/java/com/github/spring/boot/javafx/font/controls/IconSolidTest.java new file mode 100644 index 0000000..ee0ccea --- /dev/null +++ b/src/test/java/com/github/spring/boot/javafx/font/controls/IconSolidTest.java @@ -0,0 +1,29 @@ +package com.github.spring.boot.javafx.font.controls; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testfx.framework.junit5.ApplicationExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(ApplicationExtension.class) +class IconSolidTest { + + @Test + void testNewInstance() { + var result = new IconSolid(); + + assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); + } + + @Test + void testNewInstanceWithUnicode() { + var unicode = IconSolid.AD_UNICODE; + + var result = new IconSolid(unicode); + + assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); + assertEquals(unicode, result.getText()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/spring/boot/javafx/font/controls/IconTest.java b/src/test/java/com/github/spring/boot/javafx/font/controls/IconTest.java new file mode 100644 index 0000000..263c8a9 --- /dev/null +++ b/src/test/java/com/github/spring/boot/javafx/font/controls/IconTest.java @@ -0,0 +1,71 @@ +package com.github.spring.boot.javafx.font.controls; + +import javafx.geometry.Insets; +import javafx.scene.paint.Color; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testfx.framework.junit5.ApplicationExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(ApplicationExtension.class) +class IconTest { + public static final String FONT_FAMILY = "FontAwesome"; + + @Test + void testNewInstance() { + var result = new Icon(); + + assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); + assertEquals(FONT_FAMILY, result.getFont().getFamily()); + } + + @Test + void testNewInstanceWithUnicode() { + var unicode = Icon.ADJUST_UNICODE; + + var result = new Icon(unicode); + + assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); + assertEquals(unicode, result.getText()); + } + + @Test + void testNewInstanceWithBuilder() { + var padding = new Insets(10, 20, 30, 40); + + var result = Icon.builder() + .unicode(Icon.AMAZON_UNICODE) + .padding(padding) + .visible(true) + .build(); + + assertTrue(result.getStyleClass().contains(AbstractIcon.STYLE_CLASS)); + assertEquals(Icon.AMAZON_UNICODE, result.getText()); + assertEquals(padding, result.getPadding()); + assertTrue(result.isVisible()); + assertEquals(FONT_FAMILY, result.getFont().getFamily(), "expected the font family to not have been changed"); + } + + @Test + void testSizeFactor() { + var icon = new Icon(); + assertEquals(0.0, icon.getSizeFactor(), "expected the initial size factor to be 0.0"); + + var newSizeFactor = 2.3; + icon.setSizeFactor(newSizeFactor); + assertEquals(newSizeFactor, icon.getSizeFactor(), "expected the size factor to be 2.3"); + assertEquals(FONT_FAMILY, icon.getFont().getFamily(), "expected the font family to not have been changed"); + } + + @Test + void testTextColor() { + var icon = new Icon(); + assertEquals(Color.BLACK, icon.getTextFill(), "expected the initial text color to be black"); + + icon.setColor(Color.RED); + assertEquals(Color.RED, icon.getTextFill(), "expected the text color to be red"); + assertEquals(FONT_FAMILY, icon.getFont().getFamily(), "expected the font family to not have been changed"); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageTest.java b/src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageTest.java new file mode 100644 index 0000000..1675b92 --- /dev/null +++ b/src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageTest.java @@ -0,0 +1,55 @@ +package com.github.spring.boot.javafx.stage; + +import javafx.application.Platform; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.testfx.framework.junit5.ApplicationExtension; +import org.testfx.util.WaitForAsyncUtils; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(ApplicationExtension.class) +class BorderlessStageTest { + @Test + void testSetHeader() { + var expectedResult = 10.0; + var stage = new AtomicReference(); + Platform.runLater(() -> stage.set(new BorderlessStage())); + WaitForAsyncUtils.waitForFxEvents(); + + stage.get().setHeader(expectedResult); + + assertEquals(expectedResult, stage.get().getHeader()); + } + + @Test + void testHeaderProperty() { + var expectedResult = 15.0; + var stage = new AtomicReference(); + var headerHolder = new AtomicReference(); + Platform.runLater(() -> stage.set(new BorderlessStage())); + WaitForAsyncUtils.waitForFxEvents(); + + stage.get().headerProperty().addListener((observable, oldValue, newValue) -> { + headerHolder.set(newValue.doubleValue()); + }); + stage.get().setHeader(expectedResult); + WaitForAsyncUtils.waitForFxEvents(); + + assertEquals(expectedResult, headerHolder.get()); + } + + @Test + void testSetResizeBorder() { + var expectedResult = 5.0; + var stage = new AtomicReference(); + Platform.runLater(() -> stage.set(new BorderlessStage())); + WaitForAsyncUtils.waitForFxEvents(); + + stage.get().setResizeBorder(expectedResult); + + assertEquals(expectedResult, stage.get().getResizeBorder()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapperTest.java b/src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapperTest.java index ac370ef..be715af 100644 --- a/src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapperTest.java +++ b/src/test/java/com/github/spring/boot/javafx/stage/BorderlessStageWrapperTest.java @@ -7,6 +7,6 @@ class BorderlessStageWrapperTest { @Test void testConstructor_whenStageArgumentIsNull_shouldThrowIllegalArgumentException() { - assertThrows(IllegalArgumentException.class, () -> new BorderlessStageWrapper(null), "stage cannot be null"); + assertThrows(NullPointerException.class, () -> new BorderlessStageWrapper(null), "stage cannot be null"); } } diff --git a/src/test/java/com/github/spring/boot/javafx/view/ViewLoaderImplTest.java b/src/test/java/com/github/spring/boot/javafx/view/ViewLoaderImplTest.java new file mode 100644 index 0000000..3f7ea35 --- /dev/null +++ b/src/test/java/com/github/spring/boot/javafx/view/ViewLoaderImplTest.java @@ -0,0 +1,59 @@ +package com.github.spring.boot.javafx.view; + +import com.github.spring.boot.javafx.JavaFxAutoConfiguration; +import com.github.spring.boot.javafx.TestConfiguration; +import com.github.spring.boot.javafx.controllers.MyTestController; +import javafx.fxml.FXML; +import javafx.scene.layout.Pane; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.testfx.framework.junit5.ApplicationExtension; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@SpringBootTest( + classes = { + JavaFxAutoConfiguration.class, + TestConfiguration.class + }, + webEnvironment = SpringBootTest.WebEnvironment.NONE +) +@ExtendWith({MockitoExtension.class, ApplicationExtension.class}) +public class ViewLoaderImplTest { + @Autowired + ViewLoader viewLoader; + @Autowired + MyTestController controller; + + @AfterEach + void tearDown() { + controller.setRoot(null); + } + + @Test + void testLoadWithoutController() { + var result = viewLoader.load("load_view_with_controller.fxml"); + + assertNotNull(result); + assertNotNull(controller.getRoot(), "expected a root node to have been wired"); + } + + @Test + void testLoadWithController() { + var controller = new MyController(); + + var result = viewLoader.load("load_view_without_controller.fxml", controller); + + assertNotNull(result); + assertNotNull(controller.root, "expected a root node to have been wired"); + } + + public static class MyController { + @FXML + Pane root; + } +} \ No newline at end of file diff --git a/src/test/resources/views/load_view_with_controller.fxml b/src/test/resources/views/load_view_with_controller.fxml new file mode 100644 index 0000000..bb70ba1 --- /dev/null +++ b/src/test/resources/views/load_view_with_controller.fxml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/test/resources/views/load_view_without_controller.fxml b/src/test/resources/views/load_view_without_controller.fxml new file mode 100644 index 0000000..6019e1f --- /dev/null +++ b/src/test/resources/views/load_view_without_controller.fxml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file From dfc6ce255b5a232222d954e7f0a186fe272e4e3f Mon Sep 17 00:00:00 2001 From: yoep Date: Sun, 21 Apr 2024 22:30:58 +0200 Subject: [PATCH 2/4] Added additional java code docs --- .../boot/javafx/JavaFxAutoConfiguration.java | 4 +++- .../javafx/font/config/FontConfiguration.java | 10 ++++++++++ .../text/config/LocaleTextConfiguration.java | 13 ++++++++++++ .../javafx/view/config/ViewConfiguration.java | 20 +++++++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/github/spring/boot/javafx/JavaFxAutoConfiguration.java b/src/main/java/com/github/spring/boot/javafx/JavaFxAutoConfiguration.java index c17b9c5..2bc727b 100644 --- a/src/main/java/com/github/spring/boot/javafx/JavaFxAutoConfiguration.java +++ b/src/main/java/com/github/spring/boot/javafx/JavaFxAutoConfiguration.java @@ -7,7 +7,9 @@ import org.springframework.context.annotation.Import; /** - * The auto configuration for JavaFX. + * Configuration class for auto-configuring JavaFX related beans. + * This class imports other configuration classes responsible for configuring + * fonts, locale text, and views in a JavaFX application. */ @Configuration @Import({ diff --git a/src/main/java/com/github/spring/boot/javafx/font/config/FontConfiguration.java b/src/main/java/com/github/spring/boot/javafx/font/config/FontConfiguration.java index 7a7b8fd..dcd59a5 100644 --- a/src/main/java/com/github/spring/boot/javafx/font/config/FontConfiguration.java +++ b/src/main/java/com/github/spring/boot/javafx/font/config/FontConfiguration.java @@ -6,8 +6,18 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * Configuration class for configuring fonts in the application. + * This class provides a bean definition for FontRegistry if one is not already present. + */ @Configuration public class FontConfiguration { + + /** + * Defines a bean for FontRegistry if no other bean of type FontRegistry is present. + * + * @return the FontRegistry bean instance + */ @Bean @ConditionalOnMissingBean(FontRegistry.class) public FontRegistry fontRegistry() { diff --git a/src/main/java/com/github/spring/boot/javafx/text/config/LocaleTextConfiguration.java b/src/main/java/com/github/spring/boot/javafx/text/config/LocaleTextConfiguration.java index 21bf054..f209a87 100644 --- a/src/main/java/com/github/spring/boot/javafx/text/config/LocaleTextConfiguration.java +++ b/src/main/java/com/github/spring/boot/javafx/text/config/LocaleTextConfiguration.java @@ -7,8 +7,21 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ResourceBundleMessageSource; +/** + * Configuration class for managing locale-specific text resources. + * This class provides a bean definition for LocaleText if one is not already present, + * using a ResourceBundleMessageSource as the underlying message source. + */ @Configuration public class LocaleTextConfiguration { + + /** + * Defines a bean for LocaleText if no other bean of type LocaleText is present, + * using the provided ResourceBundleMessageSource as the message source. + * + * @param messageSource the ResourceBundleMessageSource instance to use for managing message sources + * @return the LocaleText bean instance + */ @Bean @ConditionalOnMissingBean(LocaleText.class) public LocaleText localeText(ResourceBundleMessageSource messageSource) { diff --git a/src/main/java/com/github/spring/boot/javafx/view/config/ViewConfiguration.java b/src/main/java/com/github/spring/boot/javafx/view/config/ViewConfiguration.java index bb2a350..204db76 100644 --- a/src/main/java/com/github/spring/boot/javafx/view/config/ViewConfiguration.java +++ b/src/main/java/com/github/spring/boot/javafx/view/config/ViewConfiguration.java @@ -11,14 +11,34 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * Configuration class for managing views in the application. + * This class provides bean definitions for ViewLoader and ViewManager + * if no beans of these types are already present. + */ @Configuration public class ViewConfiguration { + + /** + * Defines a bean for ViewLoader if no other bean of type ViewLoader is present. + * + * @param applicationContext the application context + * @param viewManager the ViewManager instance + * @param localeText the LocaleText instance + * @return the ViewLoader bean instance + */ @Bean @ConditionalOnMissingBean(ViewLoader.class) public ViewLoader viewLoader(ApplicationContext applicationContext, ViewManager viewManager, LocaleText localeText) { return new ViewLoaderImpl(applicationContext, viewManager, localeText); } + /** + * Defines a bean for ViewManager if no other bean of type ViewManager is present. + * + * @param applicationContext the configurable application context + * @return the ViewManager bean instance + */ @Bean @ConditionalOnMissingBean(ViewManager.class) public ViewManager viewManager(ConfigurableApplicationContext applicationContext) { From d74965e759427be95f4e7d5e5b0567a5df8ff03e Mon Sep 17 00:00:00 2001 From: yoep Date: Sun, 21 Apr 2024 22:33:23 +0200 Subject: [PATCH 3/4] Bumped dependency versions --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 4cbdf83..cab3c16 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.0 + 3.2.5 com.github.yoep @@ -48,7 +48,7 @@ 17 - 21.0.1 + 22.0.1 5.10.1 From cdeb2e138a519e06800c84936dfcaf988d214690 Mon Sep 17 00:00:00 2001 From: yoep Date: Sun, 21 Apr 2024 22:39:01 +0200 Subject: [PATCH 4/4] Bumped test dependency versions --- .github/workflows/{maven-build.yml => build.yml} | 8 +++----- pom.xml | 11 +++++++++-- 2 files changed, 12 insertions(+), 7 deletions(-) rename .github/workflows/{maven-build.yml => build.yml} (84%) diff --git a/.github/workflows/maven-build.yml b/.github/workflows/build.yml similarity index 84% rename from .github/workflows/maven-build.yml rename to .github/workflows/build.yml index d6e4b70..194ad2c 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/build.yml @@ -12,14 +12,12 @@ on: inputs: {} jobs: - build: + test: + name: Test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - name: Set up JDK 17 uses: actions/setup-java@v3 with: @@ -28,4 +26,4 @@ jobs: cache: 'maven' - name: Maven test run: | - mvn -B test \ No newline at end of file + xvfb-run -a mvn -B test \ No newline at end of file diff --git a/pom.xml b/pom.xml index cab3c16..09ffafe 100644 --- a/pom.xml +++ b/pom.xml @@ -51,9 +51,9 @@ 22.0.1 - 5.10.1 + 5.10.2 5.2.0 - 5.7.0 + 5.11.0 4.0.17 @@ -194,6 +194,13 @@ + + + maven_central + Maven Central + https://repo.maven.apache.org/maven2/ + +