From 1f9f92ce05fb6286305e651296b5238f940117a7 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 7 Jan 2026 16:59:24 +0100 Subject: [PATCH 1/2] feat(test/integration): init basic crud tests (4.1) --- integration-tests/pom.xml | 83 +++++++++++++++++++ .../src/test/java/csep/TestModule.java | 39 +++++++++ .../src/test/java/csep/backlog/BaseTest.java | 73 ++++++++++++++++ .../java/csep/backlog/basic/CRUDTest.java | 65 +++++++++++++++ .../csep/backlog/basic/RecipeDetailsTest.java | 4 + .../resources/application-test.properties | 18 ++++ pom.xml | 4 +- 7 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 integration-tests/pom.xml create mode 100644 integration-tests/src/test/java/csep/TestModule.java create mode 100644 integration-tests/src/test/java/csep/backlog/BaseTest.java create mode 100644 integration-tests/src/test/java/csep/backlog/basic/CRUDTest.java create mode 100644 integration-tests/src/test/java/csep/backlog/basic/RecipeDetailsTest.java create mode 100644 integration-tests/src/test/resources/application-test.properties diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml new file mode 100644 index 0000000..7fab6b6 --- /dev/null +++ b/integration-tests/pom.xml @@ -0,0 +1,83 @@ + + + 4.0.0 + + csep + root + 0.0.1-SNAPSHOT + + integration-tests + + + 25 + 25 + UTF-8 + + + + + org.testfx + testfx-core + 4.0.16-alpha + + + + + org.testfx + testfx-junit5 + 4.0.16-alpha + + + + + org.openjfx + javafx-controls + 25.0.1 + + + + org.openjfx + javafx-fxml + 25.0.1 + + + + org.junit.jupiter + junit-jupiter-api + 5.10.1 + + + org.junit.jupiter + junit-jupiter-engine + 5.10.1 + + + csep + commons + 0.0.1-SNAPSHOT + + + csep + client + 0.0.1-SNAPSHOT + + + csep + server + 0.0.1-SNAPSHOT + + + org.springframework.boot + spring-boot-test + 3.5.7 + + + org.springframework.boot + spring-boot-test-autoconfigure + 3.5.7 + test + + + \ No newline at end of file diff --git a/integration-tests/src/test/java/csep/TestModule.java b/integration-tests/src/test/java/csep/TestModule.java new file mode 100644 index 0000000..1a16863 --- /dev/null +++ b/integration-tests/src/test/java/csep/TestModule.java @@ -0,0 +1,39 @@ +package csep; + +import client.scenes.FoodpalApplicationCtrl; +import client.scenes.MainCtrl; +import client.scenes.SearchBarCtrl; +import client.scenes.recipe.IngredientListCtrl; +import client.scenes.recipe.RecipeStepListCtrl; +import client.utils.*; +import com.google.inject.AbstractModule; +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.Scopes; +import com.google.inject.TypeLiteral; +import commons.Ingredient; +import commons.Recipe; + +import java.nio.file.Path; + +public class TestModule implements Module { + @Override + public void configure(Binder binder) { + binder.bind(MainCtrl.class).in(Scopes.SINGLETON); + binder.bind(FoodpalApplicationCtrl.class).in(Scopes.SINGLETON); + binder.bind(IngredientListCtrl.class).in(Scopes.SINGLETON); + binder.bind(RecipeStepListCtrl.class).in(Scopes.SINGLETON); + binder.bind(SearchBarCtrl.class).in(Scopes.SINGLETON); + binder.bind(LocaleManager.class).in(Scopes.SINGLETON); + binder.bind(ServerUtils.class).in(Scopes.SINGLETON); + binder.bind(WebSocketUtils.class).in(Scopes.SINGLETON); + + binder.bind(ConfigService.class).toInstance(new ConfigService(Path.of("config.json"))); + binder.bind(new TypeLiteral>() {}).toInstance( + new WebSocketDataService<>() + ); + binder.bind(new TypeLiteral>() {}).toInstance( + new WebSocketDataService<>() + ); + } +} diff --git a/integration-tests/src/test/java/csep/backlog/BaseTest.java b/integration-tests/src/test/java/csep/backlog/BaseTest.java new file mode 100644 index 0000000..cd999bb --- /dev/null +++ b/integration-tests/src/test/java/csep/backlog/BaseTest.java @@ -0,0 +1,73 @@ +package csep.backlog; + +import client.MyFXML; +import client.MyModule; +import client.UI; +import com.google.inject.Injector; +import commons.Recipe; +import javafx.scene.control.ListView; +import javafx.scene.input.KeyCode; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static com.google.inject.Guice.createInjector; +import static org.junit.jupiter.api.Assertions.*; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.ActiveProfiles; +import org.testfx.api.FxToolkit; +import org.testfx.framework.junit5.ApplicationTest; +import org.testfx.util.WaitForAsyncUtils; +import server.Main; +import server.WebSocketConfig; +import server.database.IngredientRepository; +import server.database.RecipeIngredientRepository; +import server.database.RecipeRepository; + +@SpringBootTest(classes = Main.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) +@ActiveProfiles("test") +@Import(WebSocketConfig + .class) +public abstract class BaseTest extends ApplicationTest { + @Autowired + private IngredientRepository ingredientRepository; + @Autowired + private RecipeRepository recipeRepository; + @Autowired + private RecipeIngredientRepository recipeIngredientRepository; + + private static final Injector INJECTOR = createInjector(new MyModule()); + private static final MyFXML FXML = new MyFXML(INJECTOR); + @Autowired + protected ApplicationContext applicationContext; + + protected T bean(Class type) { + return applicationContext.getBean(type); + } + + protected T bean(String name, Class type) { + return applicationContext.getBean(name, type); + } + @BeforeEach + public void setup() throws Exception { + ingredientRepository.deleteAll(); + recipeRepository.deleteAll(); + + recipeIngredientRepository.deleteAll(); + ingredientRepository.flush(); + recipeRepository.flush(); + recipeIngredientRepository.flush(); + FxToolkit.registerPrimaryStage(); + FxToolkit.setupApplication(UI::new); + WaitForAsyncUtils.waitForFxEvents(); + } + @AfterEach + public void tearDown() throws Exception { + FxToolkit.cleanupStages(); + } +} + diff --git a/integration-tests/src/test/java/csep/backlog/basic/CRUDTest.java b/integration-tests/src/test/java/csep/backlog/basic/CRUDTest.java new file mode 100644 index 0000000..7796d46 --- /dev/null +++ b/integration-tests/src/test/java/csep/backlog/basic/CRUDTest.java @@ -0,0 +1,65 @@ +package csep.backlog.basic; + +import commons.Recipe; +import csep.backlog.BaseTest; +import javafx.scene.control.ListView; +import javafx.scene.input.KeyCode; +import org.junit.jupiter.api.Test; +import org.testfx.util.WaitForAsyncUtils; +import server.database.RecipeRepository; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + + +// TODO(1): Testing for error paths + +public class CRUDTest extends BaseTest { + @Test + public void newRecipeOnClickDoesAddOne() { + ListView list = lookup("#recipeList").query(); + // when: + clickOn("#addRecipeButton"); + // then: + WaitForAsyncUtils.waitForFxEvents(); + assertEquals(1L, bean(RecipeRepository.class).count()); + assertEquals(1, list.getItems().size()); + } + @Test + public void deleteRecipeOnClickDoesDeleteOne() { + ListView list = lookup("#recipeList").query(); + // when: + clickOn("#addRecipeButton"); + WaitForAsyncUtils.waitForFxEvents(); + assertEquals(1, list.getItems().size()); + WaitForAsyncUtils.waitForFxEvents(); + clickOn("#removeRecipeButton"); + // then: + WaitForAsyncUtils.waitForFxEvents(); + assertEquals(0, list.getItems().size()); + } + @Test + public void changeRecipeNameDoesChangeName() { + ListView list = lookup("#recipeList").query(); + clickOn("#addRecipeButton"); + write("TEST_RECIPE"); + type(KeyCode.ENTER); + WaitForAsyncUtils.waitForFxEvents(); + assertEquals("TEST_RECIPE", list.getItems().get(0).getName()); + } + @Test + public void cloneRecipeOnClickDoesCloneOne() { + ListView list = lookup("#recipeList").query(); + clickOn("#addRecipeButton"); + write("TEST_RECIPE"); + type(KeyCode.ENTER); + WaitForAsyncUtils.waitForFxEvents(); + clickOn("#cloneRecipeButton"); + WaitForAsyncUtils.waitForFxEvents(); + assertEquals(2, list.getItems().size()); + Recipe orig = list.getItems().get(0); + Recipe cloned = list.getItems().get(1); + assertNotEquals(orig.getName(), cloned.getName()); + assertEquals(orig.getPreparationSteps(), cloned.getPreparationSteps()); + } +} diff --git a/integration-tests/src/test/java/csep/backlog/basic/RecipeDetailsTest.java b/integration-tests/src/test/java/csep/backlog/basic/RecipeDetailsTest.java new file mode 100644 index 0000000..d6d581e --- /dev/null +++ b/integration-tests/src/test/java/csep/backlog/basic/RecipeDetailsTest.java @@ -0,0 +1,4 @@ +package csep.backlog.basic; + +public class RecipeDetailsTest { +} diff --git a/integration-tests/src/test/resources/application-test.properties b/integration-tests/src/test/resources/application-test.properties new file mode 100644 index 0000000..2d65ed8 --- /dev/null +++ b/integration-tests/src/test/resources/application-test.properties @@ -0,0 +1,18 @@ +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect + +# use one of these alternatives... +# ... purely in-memory, wiped on restart, but great for testing +spring.datasource.url=jdbc:h2:mem:testdb +# ... persisted on disk (in project directory) +#spring.datasource.url=jdbc:h2:file:./h2-database + +# enable DB view on http://localhost:8080/h2-console +spring.h2.console.enabled=true + +# strategy for table (re-)generation +spring.jpa.hibernate.ddl-auto=create-drop +# show auto-generated SQL commands +#spring.jpa.hibernate.show_sql=true diff --git a/pom.xml b/pom.xml index 8c0393a..babe7e4 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,6 @@ commons client server - - + integration-tests + From 27f03d7a0ae3c31c4ef03f2809fcddbc85508902 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 13 Jan 2026 18:48:10 +0100 Subject: [PATCH 2/2] chore: wait a bit more --- integration-tests/src/test/java/csep/backlog/BaseTest.java | 1 + .../src/test/java/csep/backlog/basic/CRUDTest.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/integration-tests/src/test/java/csep/backlog/BaseTest.java b/integration-tests/src/test/java/csep/backlog/BaseTest.java index cd999bb..423264a 100644 --- a/integration-tests/src/test/java/csep/backlog/BaseTest.java +++ b/integration-tests/src/test/java/csep/backlog/BaseTest.java @@ -68,6 +68,7 @@ public abstract class BaseTest extends ApplicationTest { @AfterEach public void tearDown() throws Exception { FxToolkit.cleanupStages(); + WaitForAsyncUtils.waitForFxEvents(); } } diff --git a/integration-tests/src/test/java/csep/backlog/basic/CRUDTest.java b/integration-tests/src/test/java/csep/backlog/basic/CRUDTest.java index 7796d46..75fa30c 100644 --- a/integration-tests/src/test/java/csep/backlog/basic/CRUDTest.java +++ b/integration-tests/src/test/java/csep/backlog/basic/CRUDTest.java @@ -40,8 +40,10 @@ public class CRUDTest extends BaseTest { } @Test public void changeRecipeNameDoesChangeName() { + WaitForAsyncUtils.waitForFxEvents(); ListView list = lookup("#recipeList").query(); clickOn("#addRecipeButton"); + WaitForAsyncUtils.waitForFxEvents(); write("TEST_RECIPE"); type(KeyCode.ENTER); WaitForAsyncUtils.waitForFxEvents();