From a82c69d21ca12bef6c40db92b60e03d461bf912c Mon Sep 17 00:00:00 2001 From: Rithvik Sriram Date: Fri, 9 Jan 2026 23:54:21 +0100 Subject: [PATCH 1/2] Add print/export functionality for recipes - Implement printRecipe() method in RecipeDetailCtrl to export recipes as text files - Add directory chooser and filename input dialogs - Integrate with existing PrintExportService for file generation - Add comprehensive unit tests for PrintExportService methods --- .../scenes/recipe/RecipeDetailCtrl.java | 49 +++++++-- .../java/client/scenes/PrintExportTest.java | 99 +++++++++++++++++++ 2 files changed, 142 insertions(+), 6 deletions(-) diff --git a/client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java b/client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java index 716a128..6fa7339 100644 --- a/client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java +++ b/client/src/main/java/client/scenes/recipe/RecipeDetailCtrl.java @@ -6,11 +6,15 @@ import client.utils.Config; import client.utils.ConfigService; import client.utils.LocaleAware; import client.utils.LocaleManager; +import client.utils.PrintExportService; import client.utils.ServerUtils; import client.utils.WebSocketDataService; import com.google.inject.Inject; import commons.Recipe; + +import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -19,11 +23,12 @@ import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.TextField; +import javafx.scene.control.TextInputDialog; import javafx.scene.input.KeyCode; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; -import org.apache.commons.lang3.NotImplementedException; +import javafx.stage.DirectoryChooser; /** * Controller for the recipe detail view. @@ -246,15 +251,47 @@ public class RecipeDetailCtrl implements LocaleAware { } /** - * Print the currently viewed recipe. + * Gives the User a download file prompt and lets the + * user change the name of the downloaded file and + * uses printExportService to create a downloadable file. */ @FXML private void printRecipe() { - // TODO: actually make it print? - throw new NotImplementedException("TODO:: Integrate with Print/Export service"); - // System.out.println("Recipe printed"); - } + if (recipe == null) { + return; // Do nothing if no recipe selected + } + // Open directory chooser + DirectoryChooser directoryChooser = new DirectoryChooser(); + directoryChooser.setTitle("Select Folder to Save Recipe"); + + File selectedDirectory = directoryChooser.showDialog( + printRecipeButton.getScene().getWindow()); + + if (selectedDirectory == null) { + return; // User cancelled + } + + // Ask for filename + TextInputDialog dialog = new TextInputDialog(recipe.getName() + ".txt"); + dialog.setTitle("Save Recipe"); + dialog.setHeaderText("Enter filename for the recipe"); + dialog.setContentText("Filename:"); + + Optional result = dialog.showAndWait(); + + if (result.isPresent()) { + String filename = result.get(); + if (!filename.endsWith(".txt")) { + filename = filename + ".txt"; + } + + // Use PrintExportService methods + String recipeText = PrintExportService.buildRecipeText(recipe); + Path dirPath = selectedDirectory.toPath(); + PrintExportService.exportToFile(recipeText, dirPath, filename); + } + } /** * Toggles the favourite status of the currently viewed recipe in the * application configuration and writes the changes to disk. diff --git a/client/src/test/java/client/scenes/PrintExportTest.java b/client/src/test/java/client/scenes/PrintExportTest.java index 3fa93ee..30e7567 100644 --- a/client/src/test/java/client/scenes/PrintExportTest.java +++ b/client/src/test/java/client/scenes/PrintExportTest.java @@ -68,5 +68,104 @@ public class PrintExportTest { ()->PrintExportService.validateFolder(filePath)); assertEquals("Given path is not a folder", i.getMessage()); } + @Test + public void buildRecipeTextWithEmptyIngredientsTest(){ + final long testRecipeId = 100L; + List preparationSteps = new ArrayList<>(); + preparationSteps.add("Just wait"); + Recipe recipe = new Recipe(testRecipeId, "Empty Recipe", new ArrayList<>(), preparationSteps); + String result = PrintExportService.buildRecipeText(recipe); + assertTrue(result.contains("Title: Empty Recipe")); + assertTrue(result.contains("Recipe ID: 100")); + assertTrue(result.contains("Ingredients: ")); + assertTrue(result.contains("1: Just wait")); + } + @Test + public void buildRecipeTextWithMultipleIngredientsTest(){ + List ingredients = new ArrayList<>(); + ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Flour")); + ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Sugar")); + ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Eggs")); + ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Butter")); + final long testRecipeId = 300L; + List steps = new ArrayList<>(); + steps.add("Mix the solid ingredients"); + steps.add("Then add the wet ingredients"); + steps.add("Bake"); + Recipe recipe = new Recipe(testRecipeId, "Cake", ingredients, steps); + String result = PrintExportService.buildRecipeText(recipe); + assertTrue(result.contains("Some Flour")); + assertTrue(result.contains("Some Sugar")); + assertTrue(result.contains("Some Eggs")); + assertTrue(result.contains("Some Butter")); + assertTrue(result.contains("3: Bake")); + } + @Test + public void exportToFileWithComplexRecipeTest() throws IOException { + List ingredients = new ArrayList<>(); + ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("tomato")); + ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("potato")); + final long testRecipeId = 500L; + List steps = new ArrayList<>(); + steps.add("cut up the ingredients"); + steps.add("Cook"); + Recipe recipe = new Recipe(testRecipeId, "stew", ingredients, steps); + String recipeData = PrintExportService.buildRecipeText(recipe); + String fileName = "stew_recipe.txt"; + PrintExportService.exportToFile(recipeData, tempDir, fileName); + Path expectedFile = tempDir.resolve(fileName); + assertTrue(Files.exists(expectedFile)); + String fileContent = Files.readString(expectedFile); + assertTrue(fileContent.contains("stew")); + + } + @Test + public void exportFileWithInvalidFolderTest(){ + Path invalidfilePath = Path.of("/invalid/folder/path"); + String recipeData = "Test Data"; + String fileName = "test.txt"; + assertThrows(IllegalArgumentException.class, + () -> PrintExportService.exportToFile(recipeData, invalidfilePath, fileName)); + } + @Test + public void exportToFileOverwritesExistingFileTest() throws IOException { + String fileName = "recipe.txt"; + Path filePath = tempDir.resolve(fileName); + Files.writeString(filePath, "Old stuff"); + String newContent = "New recipe data"; + PrintExportService.exportToFile(newContent, tempDir, fileName); + String fileContent = Files.readString(filePath); + assertEquals(newContent, fileContent); + } + @Test + public void exportToFileSuccessTest() throws IOException { + String recipeData = "Test Recipe Content"; + String fileName = "test_recipe.txt"; + PrintExportService.exportToFile(recipeData, tempDir, fileName); + Path expectedFile = tempDir.resolve(fileName); + assertTrue(Files.exists(expectedFile)); + String fileContent = Files.readString(expectedFile); + assertEquals(recipeData, fileContent); + } + @Test + public void buildRecipeTextWithEmptyStepsTest(){ + List ingredients = new ArrayList<>(); + ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Water")); + final long testRecipeId = 200L; + Recipe recipe = new Recipe(testRecipeId, "Water", ingredients, new ArrayList<>()); + String result = PrintExportService.buildRecipeText(recipe); + assertTrue(result.contains("Title: Water")); + assertTrue(result.contains("Recipe ID: 200")); + assertTrue(result.contains("Ingredients: Some Water,")); + assertTrue(result.contains("Steps:")); + } + @Test + public void validateFolderWithGarbagePathTest(){ + Path garbagePath = Path.of("/this/path/does/not/exist/at/all"); + IllegalArgumentException i = assertThrows(IllegalArgumentException.class, + ()->PrintExportService.validateFolder(garbagePath)); + assertEquals("Folder does not exist", i.getMessage()); + } + } From 069e60ef7101d01989354e26327350809f19e3ef Mon Sep 17 00:00:00 2001 From: Rithvik Sriram Date: Wed, 14 Jan 2026 23:12:03 +0100 Subject: [PATCH 2/2] Refactored tests based on new constructor for recipes --- .../java/client/scenes/PrintExportTest.java | 62 +++++++++++++------ 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/client/src/test/java/client/scenes/PrintExportTest.java b/client/src/test/java/client/scenes/PrintExportTest.java index 00a85ae..69a293f 100644 --- a/client/src/test/java/client/scenes/PrintExportTest.java +++ b/client/src/test/java/client/scenes/PrintExportTest.java @@ -62,56 +62,69 @@ public class PrintExportTest { assertEquals("Given path is not a folder", i.getMessage()); } @Test - public void buildRecipeTextWithEmptyIngredientsTest(){ - final long testRecipeId = 100L; - List preparationSteps = new ArrayList<>(); - preparationSteps.add("Just wait"); - Recipe recipe = new Recipe(testRecipeId, "Empty Recipe", new ArrayList<>(), preparationSteps); - String result = PrintExportService.buildRecipeText(recipe); - assertTrue(result.contains("Title: Empty Recipe")); - assertTrue(result.contains("Recipe ID: 100")); - assertTrue(result.contains("Ingredients: ")); - assertTrue(result.contains("1: Just wait")); - } - @Test - public void buildRecipeTextWithMultipleIngredientsTest(){ + public void buildRecipeTextWithMultipleIngredientsTest() { List ingredients = new ArrayList<>(); ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Flour")); ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Sugar")); ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Eggs")); ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Butter")); + final long testRecipeId = 300L; + List steps = new ArrayList<>(); steps.add("Mix the solid ingredients"); steps.add("Then add the wet ingredients"); steps.add("Bake"); - Recipe recipe = new Recipe(testRecipeId, "Cake", ingredients, steps); + + Recipe recipe = new Recipe( + testRecipeId, + "Cake", + "Test description", + ingredients, + steps + ); + String result = PrintExportService.buildRecipeText(recipe); + assertTrue(result.contains("Some Flour")); assertTrue(result.contains("Some Sugar")); assertTrue(result.contains("Some Eggs")); assertTrue(result.contains("Some Butter")); assertTrue(result.contains("3: Bake")); } + @Test public void exportToFileWithComplexRecipeTest() throws IOException { List ingredients = new ArrayList<>(); ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("tomato")); ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("potato")); + final long testRecipeId = 500L; + List steps = new ArrayList<>(); steps.add("cut up the ingredients"); steps.add("Cook"); - Recipe recipe = new Recipe(testRecipeId, "stew", ingredients, steps); + + Recipe recipe = new Recipe( + testRecipeId, + "stew", + "Test description", + ingredients, + steps + ); + String recipeData = PrintExportService.buildRecipeText(recipe); String fileName = "stew_recipe.txt"; + PrintExportService.exportToFile(recipeData, tempDir, fileName); + Path expectedFile = tempDir.resolve(fileName); assertTrue(Files.exists(expectedFile)); + String fileContent = Files.readString(expectedFile); assertTrue(fileContent.contains("stew")); - } + @Test public void exportFileWithInvalidFolderTest(){ Path invalidfilePath = Path.of("/invalid/folder/path"); @@ -141,17 +154,28 @@ public class PrintExportTest { assertEquals(recipeData, fileContent); } @Test - public void buildRecipeTextWithEmptyStepsTest(){ + public void buildRecipeTextWithEmptyStepsTest() { List ingredients = new ArrayList<>(); ingredients.add(DefaultValueFactory.getDefaultVagueIngredient("Water")); + final long testRecipeId = 200L; - Recipe recipe = new Recipe(testRecipeId, "Water", ingredients, new ArrayList<>()); + + Recipe recipe = new Recipe( + testRecipeId, + "Water", + "Test description", + ingredients, + new ArrayList<>() + ); + String result = PrintExportService.buildRecipeText(recipe); + assertTrue(result.contains("Title: Water")); assertTrue(result.contains("Recipe ID: 200")); - assertTrue(result.contains("Ingredients: Some Water,")); + assertTrue(result.contains("Ingredients: Some Water")); assertTrue(result.contains("Steps:")); } + @Test public void validateFolderWithGarbagePathTest(){ Path garbagePath = Path.of("/this/path/does/not/exist/at/all");