From a82c69d21ca12bef6c40db92b60e03d461bf912c Mon Sep 17 00:00:00 2001 From: Rithvik Sriram Date: Fri, 9 Jan 2026 23:54:21 +0100 Subject: [PATCH] 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()); + } + }