By: Team Notably
Since: Feb 2020
Licence: MIT
1. Setting up
Refer to the guide here.
2. Design
The .puml files used to create diagrams in this document can be found in the diagrams folder.
Refer to the Using PlantUML guide to learn how to create and edit diagrams.
|
2.1. Architecture
2.1.1. Design pattern and data flow
The App is built following the Model-View-Controller design pattern.
In addition, the App’s data flow is unidirectional. That is, all user interactions in View
will trigger an appropriate handler in Logic
, which in turn updates Model
and Storage
.
Any data/state changes in Model
will then propagate back to View
automatically through JavaFX’s Property and Binding. In other words, the Observer design pattern is employed; Model
is the observable while View
is the observer.
2.1.2. Architecture-level components
Overall, the App consists of five main components:
-
Defines its API in an
interface
with the same name as the itself, e.g.Logic.java
-
Exposes its functionality using a
{Component Name}Manager
class, e.g.LogicManager.java
The following classes from Commons
plays an important role at the architecture level:
2.1.3. How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command new -t Notably
.
new -t Notably
command2.2. View
API : View.java
The View consists of a MainWindow
that is made up of parts e.g.CommandBox
, SuggestionsList
, SideBarTree
, HelpModal
, BlockContent
etc. All these, including the MainWindow
, inherit from the abstract ViewPart
class.
The View
component uses JavaFx framework. The layout of these View 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 View
component,
-
Executes user commands using the
Logic
component. -
Listens for changes to
Model
data so that the View can be updated with the modified data.
2.3. Logic
API :
Logic.java
Logic
consists of 3 subcomponents:
-
NotablyParser
: Main parser of the App, deals with user command execution. -
SuggestionEngine
: Deals with suggestions generation. -
CorrectionEngine
: Deals with auto-correction.
2.3.1. NotablyParser component
-
Logic
uses theNotablyParser
class to parse the user command. -
This results in a
List<Command>
object which is executed by theLogicManager
. -
The command execution can affect the
Model
(e.g. adding a Note). -
The updated model/data structure will automatically be reflected on to the
View
.
Given below is the Sequence Diagram for interactions within the Logic
component for the execute("delte -t cs2103")
API call.
delte -t cs2103
Command
The lifeline for DeleteCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
2.3.2. SuggestionEngine
API :
SuggestionEngine.java
SuggestionEngine
gives users the meaning of the command they input and a list of notes suggestions that they want to
open, delete, or search.
-
Logic
uses theSuggestionEngine
class, to handle the user input. -
According to the command the user inputs,
SuggestionEngine
will create aXYZSuggestionArgHandler
orABCSuggestionHandler
object which implementsSuggestionArgHandler
andSuggestionHandler
interface respectively.XYZSuggestionArgHandler
are for commands that require argument parsing, i.e.open
,delete
,search
,new
, whereasABCSuggestionHandler
are for commands that do not require argument parsing, i.e.edit
,exit
,help
. -
If
SuggestionArgHandler
object is created: theresponseText
in theModel
will be updated. This case will also result in the creation ofXYZSuggestionGenerator
object (except fornew
command) which implementsSuggestionGenerator
interface.XYZSuggestionGenerator
is then executed by theSuggestionEngine
. -
If
SuggestionHandler
object is created: theresponseText
in theModel
will be updated. -
The
Model
could be affected in 2 ways:-
Update
responseText
of theModel
(by theSuggestionHandler
andSuggestionArgHandler
): for instance, the inputopen /
will set theresponseText
in theModel
as "Open a note". -
Store a list of
SuggestionItem
in theModel
(by theSuggestionGenerator
).
-
-
The UI will then be able to retrieve the
responseText
and list ofSuggestionItem
from theModel
to be displayed to the user.
Given below is the Sequence Diagram for interactions within the Logic
and Suggestion
component for the input opne /a
.
opne /a
The lifeline for OpenSuggestionArgHandler should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
2.3.3. CorrectionEngine
The CorrectionEngine
component revolves around two API s, namely:
-
The
CorrectionEngine
interface, implemented byStringCorrectionEngine
andAbsolutePathCorrectionEngine
. Concrete implementations ofCorrectionEngine
are employed to correct an uncorrected user input. -
The
EditDistanceCalculator
interface, implemented byLevenshteinDistanceCalculator
. Concrete implementations ofEditDistanceCalculator
are employed to calculate the edit distance between two strings.
Given below is the Sequence Diagram for interactions within the StringCorrectionEngine
(one concrete implementation of CorrectionEngine
) component for the correct("uncorrected")
API call.
correct("uncorrected")
call2.4. Model
API : Model.java
The Model
,
-
stores and manipulates the
BlockTree
data that represents a tree of Blocks, through BlockModel -
stores and manipulates a list of suggestions based on the user’s input, through SuggestionModel
-
stores the current state of the
View
, through ViewStateModel-
stores the command input given by the user, through CommandInputModel
-
stores the state of the
help
modal being open, through HelpFlagModel -
stores the state of the current block’s
edit
modal being open, through BlockEditFlagModel
-
-
stores
UserPref
data that represents the user’s preferences, through UserPrefModel
2.4.1. BlockModel component
API : BlockModel.java
The BlockModel
-
stores and directly manipulates the
BlockTree
-
contains a single
BlockTreeItem
as theroot
; the tree is built by adding chilrenBlockTreeItems
to theroot
-
each
BlockTreeItem
stores the reference to its parent and childrenBlockTreeItems
, and its own content, throughTreeItem<Block>
-
stores its content, through
Block
-
contains the
Title
andBody
content
-
-
-
-
2.5. Storage
API : Storage.java
The Storage
component,
-
can save
UserPref
objects in JSON format and read it back. -
can save Notably’s
BlockModel
data in JSON format and read it back.-
stores the
BlockTree
and also the path of the last openedBlock
-
2.6. Commons
Classes used by multiple components are in the com.notably.commons
package.
2.7. Compiler
The Compiler
component’s primary usage is to compile Markdown to HTML.
Our Compiler
component’s design is based off the parsing strategy explained in GitHub’s GFM Specification
Mainly, the Compiler
component consists of the following classes:
-
Compiler
, which deals with the end-to-end job of compiling unprocessed Markdown to HTML. -
Parser
, which deals with creating an Abstract Sytax Tree representation of an unprocessed Markdown. -
Block
, which is an abstract class representing a node in a Markdown Abstract Syntax Tree. All concrete implementations of nodes in a Markdown Abstract Syntax Tree inherit from this class.
The concrete implementations of the Block
class consist of:
-
DocumentBlock
, which represents the root of the Markdown Abstract Syntax Tree. -
HeaderBlock
, which represents a Markdown ATX heading component. -
ListBlock
, which represents a Markdown unordered list. -
ListItemBlock
, which represents a Markdown list item. -
ParagraphBlock
, which represents a Markdown paragraph. -
TextBlock
, which represents plain text in Markdown.
Two of Block
's abstract methods are particularly important:
-
Block#next
: This method should be implemented by each ofBlock
's implementation in such a way that accepts a singleString
line and evolve the current Markdown Abstract Syntax Tree further. That way, each ofBlock
's implementation only needs to care about processing the portion of theString
line that is relevant to them, before delegating the rest to its childrenBlock
s. -
Block#toHtml
: This method should be implemented by each ofBlock
's implementation in such a way that it returns the HTML representation of the currentBlock
. That way, each ofBlock
's implementation only needs to care about generating its own HTML; the rest can be delegated to its childrenBlock
s.
In short, our Compiler
class will first call the Parser#parse
method to generate a Markdown Abstract Syntax Tree.
After that, the Compiler
class will transform the returned Markdown Abstract Syntax Tree into HTML by calling the root DocumentBlock
's toHtml
method (which will in turn invoke each of its children’s toHtml
method).
Given below is the Sequence Diagram for interactions within the Compiler
component for the compile(markdown)
API call.
compile(markdown)
call2.8. Logging
We are using java.util.logging
package for logging. The LogsCenter
class is used to manage the logging levels and logging destinations.
-
The logging level can be controlled using the
logLevel
setting in the configuration file (See Section 2.9, “Configuration”) -
The
Logger
for a class can be obtained usingLogsCenter.getLogger(Class)
which will log messages according to the specified logging level -
Currently log messages are output through:
Console
and to a.log
file.
Logging Levels
-
SEVERE
: Critical problem detected which may possibly cause the termination of the application -
WARNING
: Can continue, but with caution -
INFO
: Information showing the noteworthy actions by the App -
FINE
: Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size
2.9. Configuration
Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json
).
3. Implementation
This section describes the details on how features are implemented.
3.1. Correction Engine
3.1.1. Rationale
CorrectionEngine
is needed to enable auto-correction of user inputs, to deliver as good typing experience as possible.
3.1.2. Current implementation
CorrectionEngine
revolves around two API s, namely:
-
The
CorrectionEngine
interface, implemented byStringCorrectionEngine
andAbsolutePathCorrectionEngine
. Concrete implementations ofCorrectionEngine
are employed to correct an uncorrected user input. -
The
EditDistanceCalculator
interface, implemented byLevenshteinDistanceCalculator
. Concrete implementations ofEditDistanceCalculator
are employed to calculate the edit distance between two strings.
Two concrete implementations of the CorrectionEngine
interface are, namely:
-
The
StringCorrectionEngine
class, which deals with the correction of plain strings. -
The
AbsolutePathCorrectionEngine
class, which deals with the correction of absolute paths. The absolute paths here refer to the address of the notes (or blocks, as we call it) that exist in the App.
3.1.3. Design considerations
-
CorrectionEngine
is built as a standalone module that can be used by bothSuggestionEngine
andParser
. This decision is made so that code duplication in relation to auto-correction is minimal. -
Both
CorrectionEngine
andEditDistanceCalculator
are implemented as interfaces, in an attempt to make the design of theCorrectionEngine
component resilient to change. This design enables us to leverage on the strategy pattern to make ourCorrectionEngine
component more future-proof.
3.2. Suggestion Engine
3.2.1. Rationale
SuggestionEngine
allows the users to traverse their notes conveniently, without having
to remember the hierarchical structure of their notes. SuggestionEngine
gives users the meaning of the command they input and a list of notes suggestions that they want to
open, delete, or search.
3.2.2. Current implementation
SuggestionArgHandler | SuggestionHandler | |
---|---|---|
Purpose |
Handles the arguments part of the user input and updates the |
Updates the |
Commands |
|
|
Suggestion Generation |
Yes, by |
No |
-
Logic
uses theSuggestionEngine
class, to handle the user input. -
According to the command the user inputs,
SuggestionEngine
will create aXYZSuggestionArgHandler
orABCSuggestionHandler
object which implementsSuggestionArgHandler
andSuggestionHandler
interface respectively.XYZSuggestionArgHandler
are for commands that require argument parsing, i.e.open
,delete
,search
,new
, whereasABCSuggestionHandler
are for commands that do not require argument parsing, i.e.edit
,exit
,help
. -
If
SuggestionArgHandler
object is created: theresponseText
in theModel
will be updated. This case will also result in the creation ofXYZSuggestionGenerator
object (except fornew
command) which implementsSuggestionGenerator
interface.XYZSuggestionGenerator
is then executed by theSuggestionEngine
. -
If
SuggestionHandler
object is created: theresponseText
in theModel
will be updated. -
The
Model
could be affected in 2 ways:-
Update
responseText
of theModel
(by theSuggestionHandler
andSuggestionArgHandler
): for instance, the inputopen /
will set theresponseText
in theModel
as "Open a note". -
Store a list of
SuggestionItem
in theModel
(by theSuggestionGenerator
).
-
-
The UI will then be able to retrieve the
responseText
and list ofSuggestionItem
from theModel
to be displayed to the user.
3.2.3. Design considerations
Aspect 1: Design with respect to the whole architecture
-
SuggestionEngine
is segregated fromParser
in order to differentiate the logic when the user has finished typing and pressed Enter (which will be handled byParser
) in contrast to when the user presses the keyboard down button and Enter to take in the suggestion item. -
In order to keep the App’s data flow unidirectional,
SuggestionEngine
will update theresponseText
(which tells the user the meaning of his command) and the list ofSuggestionItem
into theModel
. Thus, by not showing theresponseText
and suggestions immediately to the UI,SuggestionEngine
will not interfere with theView
functionality. -
SuggestionArgHandler
,SuggestionHandler
,SuggestionGenerator
,SuggestionItem
, andSuggestionModel
are implemented as interfaces, in an attempt to make the design of theSuggestionEngine
component resilient to change.
Aspect 2: Implementation of suggestions generation
-
Alternative 1: Have a
SuggestionCommandParser
interface andSuggestionCommand
interface to parse each of the command, updateresponseText
in theModel
, and give suggestions.-
Pros: This provides a consistency for all the commands, where each command has a
XYZSuggestionCommandParser
andXYZSuggestionCommand
class. -
Cons: The
SuggestionCommandParsers
of the commands that do not require parsing of user input (edit
,exit
,help
) end up passing auserInput
argument that is not being used anywhere, which makes this design unintuitive. Moreover, since the updating of theresponseText
in theModel
can be done in eachSuggestionCommandParser
, theSuggestionCommand`s of `edit
,exit
, andhelp
end up to be redundant.
-
-
Alternative 2 (current choice): Create 2 separate interface to handle commands with input parsing and those without, and name it as a
SuggestionArgHandler
andSuggestionHandler
respectively.-
Pros: This solves the cons discussed in Alternative 1, as this design gives a separate implementation for the commands with input parsing and those without. It does not force the
Handler
to parse the user input when there is no need to. The namingHandler
also does not restrict the functionality of the interface and classes to just parse an input, but allows for a flexibility in executing other functionality such as updating theresponseText
in theModel
.
-
3.3. Correction Engine
3.3.1. Rationale
CorrectionEngine
is needed to enable auto-correction of user inputs, to deliver as good typing experience as possible.
3.3.2. Current implementation
The CorrectionEngine
component revolves around two API s, namely:
-
The
CorrectionEngine
interface, implemented byStringCorrectionEngine
andAbsolutePathCorrectionEngine
. Concrete implementations ofCorrectionEngine
are employed to correct an uncorrected user input. -
The
EditDistanceCalculator
interface, implemented byLevenshteinDistanceCalculator
. Concrete implementations ofEditDistanceCalculator
are employed to calculate the edit distance between two strings.
Two concrete implementations of the CorrectionEngine
interface are, namely:
-
The
StringCorrectionEngine
class, which deals with the correction of plain strings. -
The
AbsolutePathCorrectionEngine
class, which deals with the correction of absolute paths. The absolute paths here refer to the address of the notes (or blocks, as we call it) that exist in the App.
3.3.3. Design considerations
-
CorrectionEngine
is built as a standalone module that can be used by bothSuggestionEngine
andParser
. This decision is made so that code duplication in relation to auto-correction is minimal. -
Both
CorrectionEngine
andEditDistanceCalculator
are implemented as interfaces, in an attempt to make the design of theCorrectionEngine
component resilient to change. This design enables us to leverage on the strategy pattern to make ourCorrectionEngine
component more future-proof.
3.4. Paths
Given below is the implementation detail of the Path feature and some alternative design considerations.
3.4.1. Current Implementation
The Path
interface represents the location of a Block
in our data structure. A path can exist in 2 forms namely :
-
AbsolutePath
-
RelativePath
An AbsolutePath
is a path that takes its reference from the root /
block.
While a RelativePath
takes it reference from the current note that is opened.
Currently the user is given the freedom to provide any of the 2 forms when using the open
, delete
command.
Given the following DataStructure below.
Using AbsolutePath
open /CS2101
and using RelativePath
open ../CS2101
would yield the same result.
3.4.2. Design Consideration
Aspect: Implementation of Path
:
-
Alternative 1(Current choice): Have 2 separate class implementing
Path
, which isAbsolutePath
andRelativePath
.-
Pros: More readable and OOP, each class can have their individual validity REGEX.
-
-
Alternative 2: Implement a single class
PathImpl
and have a boolean flagisAbsolute
to tell if its a Relative or Absolute path.
Aspect: Logical equivalence of RelativePath
:
-
Alternative 1(Current choice): Relative path
CS2103/../note1
would be equivalent tonote1
.-
Pros: More intuitive for the user and developer, making it easier to integrate paths with other features.
-
-
Alternative 2: Relative path
CS2103/../note1
would not be logically equivalent tonote1
.
3.5. Tree Data Structure
Notably aims to provide end user a neat and well-organized workspace to store their notes. This is done by creating a tree structure; allowing users to create folder-like paths to organize their notes and group them into categories to their own liking.
3.5.1. Rationale
While this can be done with a linear data structure (a simple list), a linear list of notes would require more work to establish the relationship between groups of notes. A tree data structure supports this better, giving a clearer distinction while also establishing a form of hierarchy (as seen in the design example below).
On top of that, observability must be ensured so that the UI can update with any changes that happen on the tree (and its nodes) and also the data within each node.
3.5.2. Current Implementation
A custom tree data structure that supports observability has been implemented. As seen here, the BlockModel
is the entry of point of manipulating the data tree. The tree (referred to as BlockTree
) is made up of tree nodes (referred to as BlockTreeItem
). The tree is observable such that if any change occurs on any of the tree’s nodes, the change event will bubble upwards to the root node. Hence, the root node serves as the entry point for the BlockTree
.
To achieve this design, a BlockTreeItem
needs to contain 3 primary components:
-
an Observable reference to its parent
-
an ObservableList of its children
-
User’s note data (referred to as
Block
data) consisting of:-
Title
of the note -
Body
content of the note (optional)
-
After multiple designs, the current implementation now has BlockTreeItem
using an underlying TreeItem<Block>
to handle the general behaviour of a tree node.
When manipulating the BlockTree
, the execution of any operation is always split in this order:
-
Get the
currentlyOpenPath
from the BlockModel -
BlockModel carries out the command required based on that
currentlyOpenPath
An example of an operation is new -t CS2103T
. To execute this, the following sequence occurs:
-
NewCommandParser
creates theBlock
that has the title 'CS2103T' and a default empty body.-
The
Title
andBody
objects are created as well
-
-
NewCommand
then calls the Model and in turn, the BlockModel to add thisBlock
to the BlockTree -
BlockModel
first obtains thecurrentlyOpenPath
to execute the operation on, i.e in this case, to add the newBlock
on the path -
BlockModel
callsBlockTree
to add theBlock
to theAbsolutePath
obtained fromBlockModel
-
BlockTree
creates aBlockTreeItem
using theBlock
parsed earlier. -
Subsequently, the underlying
TreeItem<Block>
is created and theBlockTreeItem
is then added to the BlockTree.
Below is a sequence diagram that demonstrates this example
3.5.3. Design Considerations
Aspect: Using JavaFX’s TreeItem<T>
vs implementing a BlockTreeItem
from scratch
Current choice: Using JavaFX’s TreeItem<T>
Pros:
-
seamless integration with JavaFX’s
TreeView
which is used in Notably’s sidebar to show the notes in a traditional file-browser-like manner -
TreeItem<T>
has the requirements ofBlockTreeItem’s
design already implemented to a usable extent -
conveniently handles underlying event handling required for
BlockTree
to be observable
Cons:
-
Implementation still requires wrapping and unwrapping of underlying
TreeItem<T>
to work withTreeView
Aspect: BlockTreeItem
vs Folders to represent path structure
Current choice: BlockTreeItem
Pros:
-
No need for an additional class. Having a separate
folder
object would also require a separate UI View since folders should not contain any block data.
Cons:
-
Somewhat unconventional design. User might be unfamiliar with the intention on first use, without proper explanation
Aspect: Root should also be a BlockTreeItem
Pros:
-
Seamless transition to JSON storage
Cons:
-
Need to add constraint to ensure that the root
BlockTreeItem
does not contain anyBody
and is also unmodifiable
3.6. Compiler
3.6.1. Rationale
Compiler
is needed to enable compilation of Markdown to HTML. By having an Markdown to HTML compiler, we can allow user to format their notes in Markdown, which enhances their note-editing experience tremendously.
3.6.2. Current implementation
The implementation of Compiler
is highly inspired by the parsing strategy explained in GitHub’s GFM Specification. Please read more from the specification for a more comprehensive explanation.
3.6.3. Design considerations
Generally speaking, compilers usually consist of several main components, namely a tokenizer, a parser, and a generator. However, this is not the case in our design of the Compiler
component:
-
Leveraging on the fact that Markdown’s syntax is not overly complicated, we decided not to fully adhere to the usual compiler design. Instead, we merged the tokenizer and parser section into our
Parser
class. ThisParser
class thus deals converting raw Markdown string into a Markdown Abstract Syntax Tree. -
In addition, we opted to not build a standalone generator component. Instead, we make it such that each node in our Markdown Abstract Syntax Tree supports a
toHtml
method, which returns the HTML representation of the tree starting from itself as a node. This way, we can leverage on OOP’s polymorphism to generate the HTML string out of our Markdown Abstract Syntax Tree a lot easier.
4. Documentation
Refer to the guide here.
5. Testing
Refer to the guide here.
6. Dev Ops
Refer to the guide here.
Appendix A: Product Scope
Target user profile:
-
Students that has a need to take notes and organize them into categories
-
prefer desktop apps over other types
-
can type fast
-
prefers typing over mouse input
-
is reasonably comfortable using CLI apps
Value proposition: Take and manage notes faster than a typical mouse/GUI driven app
Appendix B: User Stories
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
|
student |
traverse my notes in a file system-like manner |
so that I can skim through my sea of notes and drafts without any problem. |
|
student |
search my notes by their content |
I won’t have to remember the exact location and title of notes. |
|
student |
reliably type search commands(not error-prone) |
focus on searching my notes rather than ensuring my commands are exact |
|
student |
can quickly make changes to a note |
so that I can update my notes with new information accurately while in class. |
|
impatient student |
alias a path to a folder |
do not have to memorise and type out the entire file structure when accessing a nested note |
Appendix C: Use Cases
(For all use cases below, the System is the Notably
and the Actor is the user
, unless specified otherwise)
Use case: Search notes using the Auto-suggestion feature
MSS
-
User types in a keyword of a note’s content that he wants to open.
-
Notably lists out the relevant search results, with the most relevant at the top of the list (based on the keyword’s number of occurrences in the note).
-
User chooses one of the suggested notes.
-
Notably opens the chosen note.
Use case ends.
Extensions
-
2a. No suggestion is being generated.
-
2a1. Notably displays a response text, indicating that the user is trying to search through all of the notes using that particular keyword.
-
2a2. Since the empty suggestion conveys that the keyword cannot be found, the user enters a new data.
-
Steps 2a1-2a2 are repeated until the data entered is correct. Use case resumes from Step 3.
Use case: Open/ Delete notes using the Auto-suggestion feature
MSS
-
User types in an incomplete path or title of a note.
-
Notably lists out suggestions of notes.
-
User chooses one of the suggested notes.
-
Notably opens/ deletes the chosen note.
Use case ends.
Extensions
-
1a. Path or title contains invalid character(s) ( symbols
-
or`
)-
1a1. Notably displays a response text, indicating that the path or title is invalid.
-
1a2. User enters a new data.
-
Steps 1a1-1a2 are repeated until the data entered is correct. Use case resumes from Step 2.
-
1b. Path or title does not exist
-
1b1. Notably displays a response text, indicating that the user is trying to open/ delete the note with the particular path or title that the user inputs.
-
1b2. Notably does not generate any suggestions, which means the note cannot be found.
-
1b3. User enters a new data.
-
Steps 1b1-1b3 are repeated until the data entered is correct. Use case resumes from Step 2.
Use case: Quickly Edit notes using the Edit Modal
MSS
-
User invokes edit command by typing "edit" in the command box.
-
Notably immediately opens an edit modal for the currently open note to allow for editing.
-
User presses a keyboard shortcut ("ESC").
-
Notably saves the user’s changes
-
Notably closes the edit modal.
Use case ends.
Extensions
-
2a. User does not make any edits to the content of the note.
Proceed to 3 first, then resume use case from 5.
-
2b. User edits the content of the note.
Use case continues from 3.
{More to be added}
Appendix D: Non Functional Requirements
-
Should work on any mainstream OS as long as it has Java
11
or above installed. -
Should be able to hold up to 1000 notes 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.
Appendix F: Instructions for Manual Testing
Given below are instructions to test the app manually.
These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing. |
F.1. Launch and Shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows the GUI with a set of sample Notes. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum 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.
-
F.2. Add a new Note
-
Adding a new Note to Notably without immediately opening that Note.
-
Prerequisites: None of the child(ren) Note(s) of the currently opened Note has the same title as the new Note.
Moreover, the currently opened Note must be the directory where the user wants to store the new Note. -
Command:
new -t Notably
Expected: a new Note titled "Notably" is added to the currently opened Note. The currently opened Note (current working directory) remains the same (highlighted at the sidebar).
-
-
Adding a new Note to Notably and immediately opening that Note
-
Prerequisites: None of the child(ren) Note(s) of the currently opened Note has the same title as the new Note.
Moreover, the currently opened Note must be the directory where the user wants to store the new Note. -
Command:
new -t Notably -o
Expected: a new Note titled "Notably" is added to the currently opened Note. The currently opened Note (current working directory) is now the newly created "Notably" note (highlighted at the sidebar).
-
F.3. Open a Note
-
Opening a Note in Notably.
-
Prerequisites: The Note that is about to be opened must not be the root Note.
-
Command:
open [-t] Notably
Expected: The Note titled "Notably" will be opened, with its content being displayed in the UI. The label "Notably" at the sidebar will also be highlighted, to indicate that the Note is being opened.
-
F.4. Delete a Note
-
Deleting a Note in Notably.
-
Prerequisites: The Note that is about to be deleted must not be the root Note.
-
Command:
delete [-t] Notably
Expected: The Note titled "Notably" will be deleted. The other nested Notes inside the Note "Notably" will also be deleted.
The Note "Notably" will no longer be visible in the sidebar.
-
F.5. Edit a Note
-
Editing a Note in Notably.
-
Prerequisites: The Note that is about to be edited must not be the root Note.
The currently opened Note must be the Note that the user wants to edit. -
Command:
edit
Expected: An edit modal will pop up displaying your Note’s content in HTML format. The user can edit and save the Note by exiting that modal.
-
F.6. Search for a Note based on a keyword
-
Searching for a note by using a keyword in Notably.
-
Prerequisites: -
-
Command:
search [-s] hello
Expected: A list of suggestions will be displayed, sorted based on the number of keyword matches, i.e. the note having the highest number of "hello" in its body will be put at the top of the suggestion list.
-
F.7. Correct command
-
Correct command word in user input.
-
Prerequisites: There should exist a Note titled Notably.
-
Command: 'delete [-t] Notably'
-
Expected: The command is interpreted as a delete command by Notably.
-
-
Correct path in user input.
-
Prerequisites: There should exist a Note titled Notably.
-
Command: 'delete [-t] Notbly'
-
Expected: The command is interpreted as a delete command by Notably.
-