Developer Guide
- Introduction
- Design
- Feature Implementation
- Appendix: Requirements
- Appendix: Instructions for User Acceptance Testing
- Conclusion
Introduction
Purpose
RIZZipe is a command-based recipe database designed with versatile tagging and searching features in mind, enabling you to always find the perfect recipe with ease. Explore RIZZipe’s diverse features and elevate your culinary experience and rizz!
This developer guide aims to detail the architecture and software design decisions behind RIZZipe, and is intended for developers, designers, and software testers of RIZZipe. As RIZZipe is built on Java 8 and JavaFX 11, some technical knowledge of Java and JavaFX is recommended when reading this developer guide.
You may peruse the libraries we used and their documentation at the following links:
- Java 8 - Java Language
- JavaFX 11 - Framework for developing Graphical User Interfaces (GUI) in Java
- Jackson - Library for serialization and deserialization of data from JavaScript Object Notation (JSON) format files
- JUnit 5 - Library for adding unit tests for code validation
Who this guide is for
This guide is intended for software developers who wish to work on RIZZipe, whether to add new features or to patch existing bugs. The source code may be found here.
Developers are free to propose changes under any of the following categories, provided the justification is stated within your Pull Request description:
- Bug Fixes
- New Features
- Adding of new Libraries/Dependencies or new Features
Tip: To get started, you may fork this repository and create a feature branch within your fork. Afterward, submit a pull request to us!
How to Use
This developer guide is broken down into 4 main sections:
- Design, which provides details on the overall design and architecture of RIZZipe,
- Feature Implementation, which covers the implementation of some notable features of RIZZipe,
- Requirements, which outlines the software requirements of RIZZipe, and
- Instructions for manual testing, which details steps that software testers can take to test RIZZipe.
Any unfamiliar RIZZipe-specific terms can be found in the glossary below.
Acknowledgements
- This project is based on the AddressBook Level 3 (AB3) project created by the SE-EDU initiative.
- Libraries used: JavaFX, JUnit5, Jackson
Design
.puml
files used to create diagrams in this document can be found in the diagrams folder.
Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.
Architecture
Below is a quick overview of the main components of the app and how they interact with each other.
To better illustrate and explain the high-level design of the App, we have also included the Architecture Diagram below.
Main components of the architecture
The Main
part of the app has 2 classes called Main
and MainApp
. Its responsibilities are:
-
On startup: Initializes all components in the appropriate sequence (storage, model, logic, UI) and connects them with each other.
-
On shutdown: Shuts down all the components and invokes cleanup methods wherever necessary.
The Commons
package represents a collection of classes used by multiple other components, storing information such as GUI settings and user-visible error messages.
The rest of the App is comprised of four components, each in their own package of related files, with the following main responsibilities:
-
UI
: The user interface of the app. -
Logic
: The command executor. -
Model
: Holds the data of the App in memory. -
Storage
: Reads data from, and writes data to, the hard disk.
How the architecture components interact with each other
The following Sequence Diagram shows how the different components interact with each other for the
scenario where the user issues the command delete 1
.
Each of the four main components as shown in the diagram above,
- defines its API in an
interface
with the same name as the Component. - implements its functionality using a concrete
{Component name}Manager
class (which follows the corresponding APIinterface
mentioned above).
For instance, the Logic
component defines its APi in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface.
Other components interact with a given component through its interface rather than the concrete class (in order to prevent external components being coupled to the implementation of a component) as illustrated in the (partial) class diagram below.
The following sections will give more details on each component.
UI component
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, RecipeListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the JavaFx UI framework. The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component,
- executes user commands using the
Logic
component. - listens for changes to
Model
data so that the UI can be updated with the modified data. - keeps a reference to the
Logic
component, because theUI
relies on theLogic
to execute commands. - and depends on some classes in the
Model
component, as it displaysRecipe
object residing in theModel
.
Logic component
The API of this component is specified in Logic.java
Here’s a (partial) class diagram of the Logic
component:
How the Logic
component works:
-
When
Logic
is called upon to execute a command, it uses the RecipeBookParser class to parse the user command. -
This results in a
Command
object (more precisely, an object of one of its subclasses e.g.,AddCommand
) which is executed by theLogicManager
. -
The command can communicate with the
Model
when it is executed (e.g. to add a recipe). -
The result of the command execution is encapsulated as a
CommandResult
object which is returned back fromLogic
.
The Sequence Diagram below illustrates the interactions within the Logic
component for the
("add n/Aglio Olio i/pasta i/pepper")
API call.
AddCommandParser
should
end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
- When called upon to parse a user command, the
RecipeBookParser
class creates anXYZCommandParser
(XYZ
is a placeholder for the specific command name e.g.,AddCommandParser
) which uses the other classes shown above to parse the user command and create aXYZCommand
object (e.g.,AddCommand
) which theRecipeBookParser
returns back as aCommand
object. - All
XYZCommandParser
classes (e.g.,AddCommandParser
,DeleteCommandParser
, …) inherit from theParser
interface so that they can be treated similarly where possible e.g, during testing.
Model component
The API of this component is specified in Model.java
The Model
component,
- stores the recipe book data i.e., all
Recipe
objects (which are contained in aUniqueRecipeBook
object). - stores the currently ‘selected’
Recipe
objects (e.g., results of a search query such asfind
orlist
) as a separate filtered list which is exposed to outsiders as an unmodifiableObservableList<Recipe>
instance that can be ‘observed’ e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list changes. - stores a
UserPref
object that represents the user’s preferences. This is exposed to the outside as aReadOnlyUserPref
object. - does not depend on any of the other three components (as the
Model
represents data entities of the domain, they should make sense on their own without depending on other components).
It is worth noting that to populate their Recipe
objects with Ingredient
instances, clients need only
pass valid IngredientBuilder
instances to Recipe
objects.
The Recipe
class will then populate its own Ingredient
and IngredientQuantifier
fields, via the given IngredientBuilder
and its use of IngredientParser
.
Tag
list and
an Ingredient
list in the RecipeBook
, which Recipe
references. This allows RecipeBook
to only require
one Tag
object per unique tag, and one Ingredient
object per unique ingredient, instead of each Recipe
needing their own Tag
or Ingredient
objects.This, however is highly complex and adds additional dependencies, which may in turn introduce more vulnerabilities or points of failure. As such, its implementation is a proposed extension feature to this project.
Storage component
The API of this component is specified
in Storage.java
Structure:
The Storage
component:
- saves both recipe data and user preferences data in JSON format.
- reads saved JSON recipe data and user preferences data into their respective objects.
- inherits from both the
RecipeBookStorage
andUserPrefStorage
interfaces, which means it can be treated as either one (if the functionality of only one is needed). - depends on some classes in the Model component (because the Storage component’s job is to save/retrieve objects that belong to the Model).
Implementation:
Serialization and deserialization of recipe book objects is done using Jackson.
To serialize a recipe, we must necessarily serialize its component fields too: its Name
, RecipePortion
,
RecipeDuration
, Tag
set, Ingredient
list, and Step
list.
The default JSON representation for each component is to express the fields of each component as key-value pairs.
However, this representation is too verbose and space-inefficient. Hence, we opted to write custom JSON adapters for
each component clas, which can be found in the seedu.recipe.storage.jsonadapters
package. These JSON adapters allow us to express how each class should be serialized.
The Common
component
This component contains classes which are used by multiple components, and are located in the seedu.recipe.commons
package.
Feature Implementation
This section describes some noteworthy details on how certain features are implemented.
Feature: “Add” Form UI
Overview
The AddRecipeForm
allows users to add new recipes directly through a Graphical User Interface (GUI) form instead of typing in the command box. The following sequence diagram illustrates how the different components interact with one another in the execution of an addf
command.
Implementation
The AddRecipeForm
class inherits from the RecipeForm
base class which extends the UiPart<Region>
class and initializes various UI components, such as TextFields
, TextAreas
that are used for displaying and editing recipe details, and Buttons
for saving and closing the form.
The RecipeForm
class has a constructor that takes a null Recipe
object, a StringBuilder
object reference created in AddFormCommand
, and the title
of the form.
The fields of the form are pre-populated with the existing recipe’s data if a non-null recipe is provided.
After the user modifies and saves the form, the
AddRecipeForm
modifies this StringBuilder
reference to capture the form’s edited
details in a format which is parseable as an Recipe
instance, by the app.
In addition, it implements the following operations:
-
RecipeForm#display
— Displays the form with corresponding UI components such as the Save button andTextField
andTextArea
rows. -
RecipeForm#getFormData
— Stores all the changed values in the form fields into aHashMap
. -
RecipeForm#collectFields
— Stores changed recipe fields in theHashMap
into the “data”StringBuilder
reference. -
RecipeForm#saveRecipe()
— Formats the current recipe by editing theStringBuilder
instance such that it contains the command string in a format that anAddCommandParser
can parse and understand. -
RecipeForm#closeForm()
— Closes the form without saving any changes.
After the user saves the form by clicking on the Save button, the
command string is passed to the AddCommandParser
class for parsing into a
RecipeDescriptor
instance, which will be saved by the app if there are no errors.
Example Usage Scenario
-
The user types
addf
in the command box and presses Enter, triggering theAddRecipeForm
to appear with empty form fields. -
The user modifies the recipe’s details in the form fields, such as changing the name, duration, portions, ingredients, steps, or tags.
-
The user clicks on the Save button, causing the
RecipeForm#saveRecipe()
method to be called. Changed values are stored in a newHashMap
calledchangedValues
. -
changedValues
is passed toAddRecipeForm#handle
, which callsRecipeForm#collectFields
to update theStringBuilder
instance value. -
The form is closed and the updated
StringBuilder
instance containing the command string is passed back toAddFormCommand
. -
If the StringBuilder instance is empty, then a
CommandException()
is thrown to indicate that the form was empty. Otherwise,AddFormCommand
callsAddCommandParser#parseToRecipeDescriptor(commandString)
to convert the command string into aRecipeDescriptor
instance. -
The
RecipeDescriptor
instance is converted to a newRecipe
instance throughRecipeDescriptor#toRecipe()
and is added to the model throughModel#addRecipe(recipeToAdd)
. This also relies on the existing infrastructure to validateRecipe
inputs, and any formatting errors will raise exceptions which will be returned to the UI of the App, to indicate the errors to the User.
Feature: “Edit” Form UI
The EditRecipeForm
allows users to make changes to a recipe details directly over a Graphical User Interface edit form instead of the command box.
The following sequence diagram illustrates how the different components interact with one another in the execution of an edit form command.
Implementation
Likewise, the EditRecipeForm
also inherits from the RecipeForm
base class and hence supports a similar set of operations
as the AddRecipeForm
. The key difference between the add and edit recipe forms is that the fields of the edit form are
pre-populated with the existing recipe’s data if a non-null recipe is provided.
Pre-filling of recipe data into the text fields is implemented through the following additional operations:
-
RecipeForm#saveInitialValues()
— Stores the initial values of the form fields in a HashMap. -
RecipeForm#populateFields()
— Prepopulates the form fields with values of current recipe.
Example Usage Scenario
-
The user selects a recipe and presses the F key, triggering the
RecipeForm
to appear with the selected recipe’s details pre-populated in the form fields. The initial values of that recipe is stored in aHashMap
namedinitialValues
. -
The user modifies the recipe’s details in the form fields, such as changing the name, duration, portions, ingredients, steps, or tags.
-
The user clicks on the Save button, causing the
RecipeForm#saveRecipe()
method to be called. This method checks which fields have been changed by comparing their current values with the data stored ininitialValues
.Changed values are stored in a new
HashMap
calledchangedValues
, which is then collected into theStringBuilder
instance throughRecipeForm#collectFields
. -
The form is closed and the new
StringBuilder
instance is returned toRecipeCard
for execution. -
The final command text is generated from the
StringBuilder
instance, and along with thedisplayedIndex
of the recipe, is passed to anEditRecipeEvent
object, which is then fired to update the model and subsequently the UI with the edited recipe details.
Activity Diagram
The following activity diagram summarizes the process when a user edits a recipe using the recipe form:
Feature: Find-by-property
Overview
The find
command allows the user to filter recipes by their properties:
e.g. their name, tags, or ingredients.
The following sequence diagram illustrates how the different components interact with each other
in the execution of a find tag italian indian
command.
Implementation
As with all commands, the find command goes through the standard command execution pipeline.
In FindCommandParser
, we determine which is the target property.
Keyword validation and predicate creation is then done depending on the target property.
To determine whether a recipe’s target property matches the given keywords, 2 predicate types are used:
-
PropertyNameContainsKeywordPredicate<T>
: checks whether some string representation of a property T matches any of the keywords.- e.g. if the property is
Name
, and we have a recipe named “Cacio e Pepe” and we are finding recipes whose name match the keywords [“Pepe”, “Cereal”], then this recipe would match.
- e.g. if the property is
-
PropertyCollectionContainsKeywordPredicate<T>
: checks whether a string representation of any property T in a collection of property T matches any of the keywords.- e.g. if the property is
Tag
, and we have a recipe with tags [“Italian”, “Breakfast”] and we are finding recipes whose tags match the keywords [“Italian”, “Indian”], then this recipe would match.
- e.g. if the property is
The use of generic types in the above predicates allows it to be implemented independent of the actual type of the property, as long as the relevant getters are supplied.
Feature: Ingredient Substitutions
Overview
The sub
command allows the user to search for commonly used substitutions for ingredients.
Implementation
The sub command likewise goes through the standard command execution pipeline.
In SubCommandParser
, we determine whether the ingredient defined by the user (to be searched up for substitutes)
is valid using the regex for ingredient names that was previously defined.
In the execution of the sub command, the (valid) ingredient is queried first in a preloaded list of substitutions. This preloaded list is stored within SubstitutionsUtil and accessed through a getter from the recipe book. Subsequently, the same ingredient is then queried in every recipe within the recipe book.
Should the ingredient be found in the preloaded list, the corresponding substitutions will be added to the list of substitutes to be returned to user. Otherwise, should the ingredient be found in any recipe in the recipe book, should any substitutes for that ingredient be stored, it will likewise be added to the list.
This list is created in the form of a HashSet
such that any duplicates will not be displayed more than once.
The display of the results will be in the command result box which is different from the other commands, since the
results of the sub
command return ingredients instead of recipes.
Feature: Import RecipeBook
Overview
The import
command allows the user to import another RecipeBook JSON file into the app, and in so doing, import new Recipes to use.
If the imported file parses correctly into a RecipeBook, the recipes in the JSON file will be successfully imported while ignoring duplicates (with respect to the App’s current Recipe Book). Else, if the file is improperly formatted, the import operation will fail and be cancelled.
Implementation
The import
command goes through the standard command execution pipeline, skipping the parser phase.
During the execution of the import command, we will call execute()
on an instance
of the ImportManager
class. This class, which is a part of the Storage
package,
allows the user to select another Recipe Book JSON file for import from their file
system. The parsing is performed using the existing parse
methods in the
seedu.recipe.commons.util.JsonUtil
class. Afterwards, we will cross-check with
our current RecipeBook to filter out duplicate recipes, and add the remaining
non-duplicate recipes.
Feature: Export RecipeBook
Overview
The export
command allows the user to export the current Recipe Book as a JSON
file, to a file system location of their choosing. This JSON file will be in a
format which is compatible with the Import feature.
Implementation
The export
command goes through the standard command execution pipeline, skipping the parser phase.
During the execution of the export command, we will call execute()
on an instance
of the ExportManager
class which forms a part of the Storage
package. This
class allows the user to select a save location for the exported JSON file.
The export function is performed by performing a duplicate save of the current Recipe Book of the app, in addition to its default save location.
If the current RecipeBook has not yet been saved, this command will first trigger
a save into the App’s default save location, then create another copy of that JSON
file for export.
The JSON formatting and export operations are performed by the parse methods in
seedu.recipe.commons.util.JsonUtil
class.
Appendix: Requirements
Glossary
Defined here are some common terms used throughout the guide, as well as their definitions:
- Mainstream OS: Windows, Linux, Unix, OS-X
- Main Success Scenario (MSS): The main or optimal flow of a use case, representing the successful completion of the user’s goal.
- Use Case: A description of a specific user goal or task and the steps required to achieve it.
- Chef: The user or operator of the application.
- Recipe: A set of instructions to prepare and cook a specific dish. Includes dish name, ingredients, serving size, preparation time and any dietary labels.
- Book: Refers to the application or system that manages the storage and retrieval of recipe data.
- Storage file: The file used by the application to store and retrieve recipe data.
- Index: Refers to the position of a specific recipe within a list of recipes, represented by a numerical value.
Product scope
Target user profile:
- needs to manage a large number of recipes and their relevant information
- prefers desktop applications to mobile applications
- types fast
- prefers typing to mouse interactions
Value proposition: manage recipes more conveniently and quickly as opposed to a typical mouse app
User stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * |
new user | see usage instructions | refer to instructions when I forget how to use the app |
* * * |
user | add a new recipe | reference it in the future |
* * * |
user | list all my existing recipes | get an overview of my whole cook book |
* * * |
user | view an existing recipe | recall details of what I have previously added |
* * * |
user | search for a recipe by name | refer to a recipe I am thinking about |
* * |
user | search for recipes by its associated tags | refer to a certain type of recipe conveniently |
* * * |
chef | search for ingredient substitutions for my missing ingredients | get an idea of possible replacements for ingredients I lack |
* * * |
user | delete an existing recipe | remove recipes I no longer like |
* * |
user | clear my recipe book | start afresh and save a new set of recipes |
* * * |
user | share my recipe book with my friends | share my exciting and innovative recipes |
* * * |
user | import my friends’ recipes | have more recipes to refer to in my own recipe book |
Use cases
Here are some use cases we have defined for our app, as well as the user interaction flow within those cases. As features are added or existing features change, do modify them to reflect the desired user interaction flows.
(For all use cases below, the Book is RIZZipe
and the Chef is the user
, unless specified otherwise)
Use case: Ask for help
MSS (Main Success Scenario)
- Chef requests for help.
-
Book shows URL to User Guide of recipe book.
Use case ends.
Use case: List all recipes
MSS
- Chef requests to list recipes.
-
Book shows a list of all recipes.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
Use case: Add a recipe
MSS
- Chef requests to add a recipe.
- Book prompts the user for the name, duration, portion, tags, ingredients and steps for the recipe.
- Chef keys in details for each section in a single input, following the specified format.
-
Book adds the recipe to a database.
Use case ends.
Extensions
- 3a. The name inputted by chef is missing.
- 3a1. Book shows an error message.
- 3a2. Book requests for the name of the recipe to be keyed in.
Use case resumes from step 3.
- 3b. The fields inputted by chef is in the wrong format.
- 3b1. Book shows an error message.
- 3b2. Book requests for the fields to be input in an appropriate format.
Use case resumes from step 3.
- 4a. The given recipe has a name that coincides with another recipe.
- 4a1. Book shows a message that states such a recipe already exists.
- 4a2. The given recipe is not added.
Use case ends.
Use case: View a recipe
MSS
- Chef requests to view a specific recipe in the list.
-
Book return the specified recipe.
Use case ends.
Extensions
-
1a. The current list is empty.
Use case ends.
-
3a. The given index is invalid.
- 3a1. Book shows an error message.
Use case resumes from step 2.
Use case: Delete a recipe
MSS
- Chef requests to list recipes.
- Book shows a list of all recipes.
- Chef requests to delete a specific recipe in the list.
-
Book deletes the recipe.
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
- 3a1. Book shows an error message.
Use case resumes from step 2.
Use case: Find a recipe by name
MSS
- Chef requests to search for all recipes which name matches a keyword.
-
Book shows a list of such recipes.
Use case ends.
Extensions
- 1a. The keyword input is invalid.
- 1a1. Book shows an error message.
Use case resumes from step 1.
-
2a. The list is empty.
Use case ends.
Use case: Find a recipe by tag
MSS
- Chef requests to search for all recipes which contain a specific tag.
- Book shows a list of such recipes.
Extensions
- 1a. The tag input is invalid.
- 1a1. Book shows an error message.
Use case resumes from step 1.
-
2a. The list is empty.
Use case ends.
Use case: Find an ingredient substitute
MSS
- Chef requests to find all substitutes for an ingredient.
-
Book shows a list of such substitutes.
Use case ends.
Extensions
- 1a. The ingredient input is invalid.
- 1a1. Book shows an error message.
Use case resumes from step 1.
- 2a. The list is empty.
- 2a1. Book shows a message that states that no substitutes exist for this ingredient.
Use case ends.
Use case: Clear recipe book
MSS
- Chef requests to clear the Book.
- The Book clears all recipes and returns to an empty state.
- Book shows a message that states that the Book has been cleared.
Extensions
-
2a. Book is already empty.
Use case resumes from step 3.
Non-Functional Requirements
Here are some non-functional requirements that the app should still maintain, to ensure a pleasant experience for our target user base.
- The app should be able to operate on any mainstream OS as long as it has Java
11
or above installed. - The app should be able to hold up to 1000 recipes without a noticeable sluggishness in performance for typical usage.
- A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
- The app should have a high level of test coverage to ensure quality.
- The app should have automated testing and deployment processes to facilitate maintenance.
- The app should have clean and well-documented code that is easy to maintain and update.
- The app should be able to tolerate system failures, and save data to the storage file with backups.
- The app should have clear and comprehensive documentation, including user manuals and technical documentation.
- The app should be easily extensible to support new features and functionality.
- The documentation should be up-to-date and accurate.
- The documentation should be accessible to all users.
Appendix: Instructions for User Acceptance Testing
Given below are instructions to test the app manually. The list below is not comprehensive, so do feel free to come up with your own test cases!
1. Launch and shutdown
Description: Verify that the app launches and closes properly, and saves the user’s preferences.
Preconditions:
- User is running Ubuntu, Windows or Mac OS-X as their Operating System.
- User has a computer environment that has Java 11 installed and can run a Java JAR app from a Terminal.
- User has a display.
Test case 1.1: Initial launch
Status: Accepted (All expected behaviour is displayed)
- Download the jar file and copy into an empty folder.
- Double-click the jar file.
Expected: Shows the GUI with a set of sample recipes. The window size may not be optimal.
Test case 1.2: Saving window preferences
Status: Accepted (All expected behaviour is displayed)
- Resize the window to an optimal size. Move the window to a different location. Close the window.
- Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
Test case 1.3: Shutdown
Status: Accepted (All expected behaviour is displayed)
- When the app is open, type
exit
into the command bar and press Enter.
Expected: The app closes.
2. Navigation
Description: Verify that basic navigation features of the app is working properly.
Preconditions:
- User is on the default MainWindow page.
Test case 2.1: Viewing recipes
Status: Accepted (All expected behaviour is displayed)
- Click on a Recipe Card once.
- Press P.
Expected: A modal opens, displaying the name, duration, portion, ingredients, steps, and tags of the given recipe.
Test case 2.2: Editing recipes via form
Status: Accepted (All expected behaviour is displayed)
- Click on a recipe card to select it.
- Press F.
Expected: A form appears, containing text input fields for name, duration, portion, ingredients, steps, and tags, as well as the Cancel and Save buttons on the bottom right.
Expected: Form fields are pre-populated with the given recipe’s data. - Edit any of the fields in the form, using the format provided in the user guide as reference.
- Click the Save button at the bottom of the form to save and exit the form.
- Scroll to the bottom of the recipe list and click on the edited recipe.
- Press P to view its details.
Expected: The recipe has been edited, with the new recipe details reflecting the edits made for each field.
3. Commands
Description: Verify that commands in the app are executed properly.
Preconditions:
- User is on the default MainWindow page.
Test case 3.1: Adding recipes via form (addf
command)
Status: Accepted (All expected behaviour is displayed)
- Type
addf
in the command box and press Enter.
Expected: A form appears, containing empty text input fields for name, duration, portion, ingredients, steps, and tags, as well as the Cancel and Save buttons on the bottom right. - Fill up the form with a sample recipe given in the user guide.
- Click the Save button at the bottom of the form to save and exit the form.
- Scroll to the bottom of the recipe list and click on the newly added recipe.
- Press P to view its details.
Expected: The newly-added recipe is present at the bottom of the recipe list, and contains exactly the fields entered in the form.
4. Working with saved data
Description: Verify that saved app data can be correctly persisted between sessions.
Preconditions:
- User is on the default MainWindow page.
Test case 4.1: Importing data
Status: Accepted (All expected behaviour is displayed)
- Press F3, or click File > Import.
Expected: A file picker window appears, allowing the user to only select JSON files. - Within the file picker window, navigate to a valid RIZZipe data file, select it, and click the Open button.
Expected: The file picker window closes, and all the recipes in the data file have been added to the recipe list.
Test case 4.2: Exporting data
Status: Accepted (All expected behaviour is displayed)
- Press F4, or click File > Export.
Expected: A file picker window appears, allowing the user to only save as JSON files. - Within the file picker window, navigate to a valid folder and enter a valid file name.
- Click the Save button.
Expected: The file picker window closes, and a file with the given filename is created in the given folder.
Conclusion
We hope that this has given you a sufficient understanding of our product, as well as the design considerations behind it. Regardless, if you have any queries, feel free to submit an issue here, and we will do our best to get back to you.