By: T16-3      Since: Aug 2018      Licence: MIT

1. Setting up

1.1. Prerequisites

  1. JDK 9 or later

    JDK 10 on Windows will fail to run tests in headless mode due to a JavaFX bug. Windows developers are highly recommended to use JDK 9.
  2. IntelliJ IDE

    IntelliJ by default has Gradle and JavaFx plugins installed.
    Do not disable them. If you have disabled them, go to File > Settings > Plugins to re-enable them.

1.2. Setting up the project in your computer

  1. Fork this repo, and clone the fork to your computer

  2. Open IntelliJ (if you are not in the welcome screen, click File > Close Project to close the existing project dialog first)

  3. Set up the correct JDK version for Gradle

    1. Click Configure > Project Defaults > Project Structure

    2. Click New…​ and find the directory of the JDK

  4. Click Import Project

  5. Locate the build.gradle file and select it. Click OK

  6. Click Open as Project

  7. Click OK to accept the default settings

  8. Open a console and run the command gradlew processResources (Mac/Linux: ./gradlew processResources). It should finish with the BUILD SUCCESSFUL message.
    This will generate all resources required by the application and tests.

1.3. Verifying the setup

  1. Run the seedu.address.MainApp and try a few commands

  2. Run the tests to ensure they all pass.

1.4. Configurations to do before writing code

1.4.1. Configuring the coding style

This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with ours but it uses a different import order from ours. To rectify,

  1. Go to File > Settings…​ (Windows/Linux), or IntelliJ IDEA > Preferences…​ (macOS)

  2. Select Editor > Code Style > Java

  3. Click on the Imports tab to set the order

    • For Class count to use import with '*' and Names count to use static import with '*': Set to 999 to prevent IntelliJ from contracting the import statements

    • For Import Layout: The order is import static all other imports, import java.*, import javax.*, import org.*, import com.*, import all other imports. Add a <blank line> between each import

Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.

1.4.2. Updating documentation to match your fork

After forking the repo, the documentation will still have the SE-EDU branding and refer to the se-edu/addressbook-level4 repo.

If you plan to develop this fork as a separate product (i.e. instead of contributing to se-edu/addressbook-level4), you should do the following:

  1. Configure the site-wide documentation settings in build.gradle, such as the site-name, to suit your own project.

  2. Replace the URL in the attribute repoURL in DeveloperGuide.adoc and UserGuide.adoc with the URL of your fork.

1.4.3. Setting up CI

Set up Travis to perform Continuous Integration (CI) for your fork. See UsingTravis.adoc to learn how to set it up.

After setting up Travis, you can optionally set up coverage reporting for your team fork (see UsingCoveralls.adoc).

Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your personal fork.

Optionally, you can set up AppVeyor as a second CI (see UsingAppVeyor.adoc).

Having both Travis and AppVeyor ensures your App works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based and AppVeyor is Windows-based)

1.4.4. Getting started with coding

When you are ready to start coding,

  1. Get some sense of the overall design by reading Section 2.1, “Architecture”.

  2. Take a look at Appendix A, Suggested Programming Tasks to Get Started.

2. Design

2.1. Architecture

Architecture
Figure 1. Architecture Diagram

The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.

The .pptx files used to create diagrams in this document can be found in the diagrams folder. To update a diagram, modify the diagram in the pptx file, select the objects of the diagram, and choose Save as picture.

Main has only one class called MainApp. It is responsible for,

  • At app launch: Initializes the components in the correct sequence, and connects them up with each other.

  • At shut down: Shuts down the components and invokes cleanup method where necessary.

Commons represents a collection of classes used by multiple other components. Two of those classes play important roles at the architecture level.

  • EventsCenter : This class (written using Google’s Event Bus library) is used by components to communicate with other components using events (i.e. a form of Event Driven design)

  • LogsCenter : Used by many classes to write log messages to the App’s log file.

The rest of the App consists of five components.

  • UI: The UI 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.

  • Session: Stores the data of the user who is logged in to the application during runtime.

Each of the five components

  • Defines its API in an interface with the same name as the Component.

  • Exposes its functionality using a {Component Name}Manager class.

For example, the Logic component (see the class diagram given below) defines it’s API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicClassDiagram
Figure 2. Class Diagram of the Logic Component

Events-Driven nature of the design

The Sequence Diagram below shows how the components interact for the scenario where the user issues the command delete 1.

SDforDeletePerson
Figure 3. Component interactions for delete 1 command (part 1)
Note how the Model simply raises a AddressBookChangedEvent when the Address Book data are changed, instead of asking the Storage to save the updates to the hard disk.

The diagram below shows how the EventsCenter reacts to that event, which eventually results in the updates being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.

SDforDeletePersonEventHandling
Figure 4. Component interactions for delete 1 command (part 2)
Note how the event is propagated through the EventsCenter to the Storage and UI without Model having to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct coupling between components.

The sections below give more details of each component.

2.2. UI component

UiClassDiagram
Figure 5. Structure of the UI Component

API : Ui.java

The UI consists of a MainWindow that is made up of parts e.g.CommandBox, ResultDisplay, PersonListPanel, StatusBarFooter, ImageView etc. All these, including the MainWindow, inherit from the abstract UiPart class.

The UI component uses 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.

  • Binds itself to some data in the Model so that the UI can auto-update when data in the Model change.

  • Responds to events raised from various parts of the App and updates the UI accordingly.

2.3. Logic component

LogicClassDiagram
Figure 6. Structure of the Logic Component

API : Logic.java

  1. Logic uses the AddressBookParser class to parse the user command.

  2. This results in a Command object which is executed by the LogicManager.

  3. The command execution can affect the Model (e.g. adding a person) and/or raise events.

  4. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("delete 1") API call.

ImplementPlvlRestrictionSequenceDiagram
Figure 7. Interactions Inside the Logic Component for the delete 1 Command

2.4. Model component

ModelClassDiagram
Figure 8. Structure of the Model Component

API : Model.java

The Model,

  • stores a UserPref object that represents the user’s preferences.

  • stores the Address Book data.

  • exposes an unmodifiable ObservableList<Person> 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 change.

  • does not depend on any of the other three components.

As a more OOP model, we can store a Tag list in Address Book, which Person can reference. This would allow Address Book to only require one Tag object per unique Tag, instead of each Person needing their own Tag object. An example of how such a model may look like is given below.

ModelClassBetterOopDiagram

2.5. Storage component

StorageClassDiagram
Figure 9. Structure of the Storage Component

API : Storage.java

The Storage component,

  • can save UserPref objects in json format and read it back.

  • can save the Address Book data in xml format and read it back.

2.6. Session component

SessionComponentClassDiagram
Figure 10. Structure of the Session Component

The Session component,

  • stores the identity of the user who is logged in to the app during runtime.

  • provides the necessary APIs for functions that require elevated user status, to inquire from SessionManager as to whether the user is logged in, and also to inquire if the user has sufficient level of access to execute that particular function.

AllPersonsHashMap is a hash map that stores all the persons from the AddressBook into the varialbe. This improves the efficiency of logging in, and also for functions that require the details of the logged in person.

2.7. Common classes

Classes used by multiple components are in the seedu.addressbook.commons package.

3. Implementation

This section describes some noteworthy details on how certain features are implemented.

3.1. Add Leave command

The leave is a command that allows the user to request leave applications. This command is only available after the user has logged in successfully.

The format of the command is: leave date/DATE

It takes in the parameter DATE in a valid format (DD/MM/YYYY).

3.1.1. Implementation

The implementation is divided in two phases.

In the first phase, parsing of the input arguments is handled by AddLeaveParser, which will create a new object AddLeaveCommand. AddLeaveParser which implements Parser interface, parses the inputted arguments from the CLI and also checks if it conforms the expected input format.

Code snippet from AddLeaveParser that shows the above:

 public AddLeaveCommand parse(String args) throws ParseException {
        ArgumentMultimap argMultimap =
                ArgumentTokenizer.tokenize(args, PREFIX_NRIC, PREFIX_DATE);
        String employeeNric = "S1234591A";

        if (!arePrefixesPresent(argMultimap, PREFIX_DATE)
                || !argMultimap.getPreamble().isEmpty()) {
            throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddLeaveCommand.MESSAGE_USAGE));
        }

        if (SessionManager.isLoggedIn()) {
            employeeNric = SessionManager.getLoggedInEmployeeNric();
        }

        EmployeeId employeeId = ParserUtil.parseEmployeeId(employeeNric);
        Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get());
        Approval status = ParserUtil.parseApproval("PENDING");

        Leave leave = new Leave (employeeId, date, status);

        return new AddLeaveCommand(leave);
    }

In the second phase, AddLeaveCommand is being executed. The AddLeaveCommand adds a Leave to the LeaveList. If the leave being added already exists in the LeaveList or when user is not logged in, executing the leave command raises a DuplicateLeaveException or UserNotLoggedInException:

public CommandResult execute(Model model, CommandHistory history) throws CommandException {
        if (isLogin && !SessionManager.isLoggedIn()) {
            throw new CommandException(STATUS_NOT_LOGGED_IN);
        } else if (model.hasLeave(toAdd)) {
            throw new CommandException(MESSAGE_DUPLICATE_LEAVE);
        } else {
            model.addLeave(toAdd);
            model.commitLeaveList();
            return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd));
        }
    }

The result of AddLeaveCommand execution is encapsulated as a CommandResult object which is passed back to the UI and display the added leave application in the leave list.

The sequence diagram below demonstrates the interaction within the Logic component of AddLeaveCommand:

AddLeaveCommandSequenceDiagram
Figure 11. Sequence diagram of AddLeaveCommand

3.1.2. Design Considerations

Aspect: Storage for leave
  • Alternative 1 (current choice): Store in a different XML file.

    • Pros: Easy to implement by duplicating existing storage, parser and XML related classes to cater for new XML file.

    • Cons: Need to spend extra time on deleting of leaves when a particular person is deleted.

  • Alternative 2: Store in a existing XML file.

    • Pros: Easier to implement, as it only requires to add onto existing parser.

    • Cons: May require extra parameters to create a Person object.

  • Alternative 3: Store in a different file format.

    • Pros: Easier to understand.

    • Cons: Consumes extra time to implement different parser from scratch.

3.2. FilterDepartment feature

3.2.1. Implementation

The filterdepartment is a command that filters out department to list a particular department employee’s list. The filterdepartment mechanism is facilitated by the FilterDepartmentCommandParser, which parses input arguments and creates a new FilterDepartmentCommand object. The FilterDepartmentCommandParser, which implements the Parser interface, parses the arguments inputted in to the CLI, and checks if the user input conforms the expected format.

Code snippet from FilterDepartmentCommandParser.java that shows the parsing of arguments and checking for invalid arguments:

public FilterDepartmentCommand parse(String args) throws ParseException {
        String trimmedArgs = args.trim();
        trimmedArgs = trimmedArgs.toLowerCase();
        if (trimmedArgs.isEmpty() || trimmedArgs.contains("management")) {
            throw new ParseException(
                    String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterDepartmentCommand.MESSAGE_USAGE));
        }
        ...
        ...
}

In the second phase, the command is being executed in FilterDepartmentCommand. The updateFilteredPersonList updates the filteredPersonList with the departments that matches the keywords use as the argument in FilterDepartmentCommand. The result of FilterDepartmentCommand execution is encapsulated as a CommandResult object and returns it to the LogicManager and subsequently to the UI and display the filtered department employee’s list.

Code snippet from FilterDepartmentCommand.java:

public class FilterDepartmentCommand extends Command {

    ...
    ...

    private final DepartmentContainsKeywordsPredicate predicate;

    public FilterDepartmentCommand(DepartmentContainsKeywordsPredicate predicate) {
        this.predicate = predicate;
    }

    @Override
    public CommandResult execute(Model model, CommandHistory history) {
        requireNonNull(model);
        model.updateFilteredPersonList(predicate);
        return new CommandResult(
                String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
    }
    ...
    ...
}

The sequence diagram below demonstrates the interaction within the Logic component of FilterDepartmentCommand:

FilterDepartmentCommandSequenceDiagram
Figure 12. Sequence diagram of FilterDepartmentCommand

3.2.2. Design Considerations

Aspect: Implementation of FilterDepartment command
  • Alternative 1 (current choice): A separate command for filter department.

    • Pros: Command works more accurately and efficiently as it focuses on the keywords related to Department.

    • Cons: The implementation of another command may be unnecessary as we can integrate find command to find for departments.

  • Alternative 2: Using find command to filter department name.

    • Pros: One command works for finding name and to filter department names.

    • Cons: The command may not work properly. For example, there is an employee whose name is Junior from Senior Management. But if we only want to filter employees who are in Junior Management, the command will output people who are in Junior Management and the employee Junior.

3.3. Check in/out feature

3.3.1. Current implementation

The checkCommand is a command that allows employees to check in and out to work so that their working hours will be recorded according to the time they checked in/out and calculate their pay accordingly.

The checkCommand mechanism is facilitated by the AddressBookParser, which parses the user input to identify the requested command and calls the particular command parser to deal with the input arguments.

The checkCommandParser, which implements the Parser interface, parses the arguments inputted in to the CLI, and checks if the user input conforms the expected format.

  • Code snippet from checkCommandParser.java that shows the parsing of arguments,checking for invalid arguments and returning a checkCommand object:

public CheckCommand parse(String args) throws ParseException {
        requireNonNull(args);
            ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MODE);

            if (!arePrefixesPresent(argMultimap, PREFIX_MODE)
            || !argMultimap.getPreamble().isEmpty()) {
                throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CheckCommand.MESSAGE_USAGE));
            }

            if (!didPrefixAppearOnlyOnce(args, PREFIX_MODE.toString())) {
                throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, CheckCommand.MESSAGE_USAGE));
            }

            Mode mode = ParserUtil.parseMode(argMultimap.getValue(PREFIX_MODE).get());

            return new CheckCommand(mode);
    }

In the second phase, the command is being executed in checkCommand. The result of checkCommand execution is encapsulated as a CommandResult object and returns it to the LogicManager and subsequently to the UI and display check in/out mode as one of the fields.

The sequence diagram below demonstrates the interaction within the Logic component of CheckCommand:

CheckCommandSequenceDiagram
Figure 13. Sequence diagram of CheckCommand

3.4. Schedule Feature

3.4.1. Implementation

The setschedule is a command that set a particular department employee’s schedule. The setschedule mechanism is facilitated by the SetScheduleCommandParser, which parses input arguments and creates a new SetScheduleCommand object. The SetScheduleCommandParser, which implements the Parser interface, parses the arguments inputted in to the CLI, and checks if the user input conforms the expected format.

Code snippet from SetScheduleCommandParser.java that shows the parsing of arguments and checking for invalid arguments:

public SetScheduleCommand parse(String args) throws ParseException {

    ...
    ...

    EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
    if (argMultimap.getValue(PREFIX_TIME_START).isPresent()
            && argMultimap.getValue(PREFIX_TIME_END).isPresent()
            && argMultimap.getValue(PREFIX_VENUE).isPresent()) {
        Schedule schedule = ParserUtil.parseSchedule(
                argMultimap.getValue(PREFIX_TIME_START).get(),
                argMultimap.getValue(PREFIX_TIME_END).get(),
                argMultimap.getValue(PREFIX_VENUE).get());

        Set<Schedule> scheduleList = new HashSet<>();
        scheduleList.add(schedule);

        editPersonDescriptor.setSchedule(scheduleList);
    }

    ...
    ...

}

The sequence diagram below demonstrates parse() function of SetScheduleCommandParser:

SetScheduleCommandParserSequenceDiagram
Figure 14. Sequence diagram of SetScheduleCommandParser

The sequence diagram below demonstrates execute() function of SetScheduleCommand:

SetScheduleCommandSequenceDiagram
Figure 15. Sequence diagram of SetScheduleCommand

3.4.2. Design Considerations

Aspect: Storage for schedule
  • Alternative 1 (current choice): Store with AddressBook XML.

    • Pros: Easier to implement (able to add onto existing parser).

    • Cons: May require extra parameters to create a Person object.

  • Alternative 2: Store in a different XML file.

    • Pros: Easy to implement (duplicate existing parser but for different XML file).

    • Cons: Consumes extra time to delete schedule when a particular person is deleted.

  • Alternative 3: Store in a different file format.

    • Pros: Easier to understand.

    • Cons: Consumes extra time to implement different parser from scratch.

3.5. Priority level restriction feature

All employee accounts contain a priority level field. Depending on the priority level that they are given, administrative operations may be executed if they have sufficient priority level required to do so.

3.5.1. Implementation

Here is a code snippet of executing the AddCommand function. In this example, adding a person requires a person to be logged in with a priority level of ADMINISTRATOR or above, in order to execute this function:

AddCommand.java
@Override
public CommandResult execute(Model model, CommandHistory history) throws CommandException {
        requireNonNull(model);
        SessionManager sessionManager = SessionManager.getInstance(model);
        /**
         * Throws exception if user is not logged in.
         */
        if (!sessionManager.isLoggedIn()) {
            throw new CommandException(SessionManager.NOT_LOGGED_IN);
        }
        /**
         * Throws exception if user does not have the required access level.
         */
        if (!sessionManager.hasSufficientPriorityLevelForThisSession(PriorityLevelEnum.ADMINISTRATOR)) {
            throw new CommandException(String.format(PriorityLevel.INSUFFICIENT_PRIORITY_LEVEL,
                    PriorityLevelEnum.ADMINISTRATOR));
        }

        // ... Complete the execution, now that user is verified to be an admin.
    }

In the above code, adding a person requires the user that is logged in to the application to have priority level of ADMINISTRATOR. Hence, a method in SessionManager will be called to ascertain if the logged in user does indeed have sufficient priority level of an ADMINISTRATOR.

The following sequence diagram below shows the flow of how the feature works:

ImplementPlvlRestrictionSequenceDiagram
Figure 16. Sequence diagram of priority level restriction feature

3.5.2. Design Considerations

Aspect: Implementation of elevated user rights

As this is a CLI-driven application, we prioritized on the speed and efficiency of getting things done over security, which will result in users having to execute multiple commands just to get an administrative command executed.

  • Alternative 1 (current choice): Able to perform administrative rights the moment a user with elevated priority level is logged in.

    • Pros: Able to immediately perform administrative functions the moment user is logged in, such as adding or deleting employees.

    • Cons: Potential security issue; user with admin rights may accidentally forget to logout, resulting in other people with ill intentions to execute administrative functions without the permission of the actual user.

  • Alternative 2: Users with elevated priority level will need to type in a command to enable "super user" status prior to executing any administrative functions.

    • Pros: Better security; company resources will be less-prone to abuse by employees with ill intentions.

    • Cons: Less efficient in getting things done. Reason being, this is a CLI-driven application. Hence, implementing this alternative means that users will need to key in multiple commands just to get administrative functions done as a trade-off to beefing up security.

3.6. 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 3.7, “Configuration”)

  • The Logger for a class can be obtained using LogsCenter.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

3.7. Configuration

Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file (default: config.json).

4. Documentation

We use asciidoc for writing documentation.

We chose asciidoc over Markdown because asciidoc, although a bit more complex than Markdown, provides more flexibility in formatting.

4.1. Editing Documentation

See UsingGradle.adoc to learn how to render .adoc files locally to preview the end result of your edits. Alternatively, you can download the AsciiDoc plugin for IntelliJ, which allows you to preview the changes you have made to your .adoc files in real-time.

4.2. Publishing Documentation

See UsingTravis.adoc to learn how to deploy GitHub Pages using Travis.

4.3. Converting Documentation to PDF format

We use Google Chrome for converting documentation to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.

Here are the steps to convert the project documentation files to PDF format.

  1. Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in the docs/ directory to HTML format.

  2. Go to your generated HTML files in the build/docs folder, right click on them and select Open withGoogle Chrome.

  3. Within Chrome, click on the Print option in Chrome’s menu.

  4. Set the destination to Save as PDF, then click Save to save a copy of the file in PDF format. For best results, use the settings indicated in the screenshot below.

chrome save as pdf
Figure 17. Saving documentation as PDF files in Chrome

4.4. Site-wide Documentation Settings

The build.gradle file specifies some project-specific asciidoc attributes which affects how all documentation files within this project are rendered.

Attributes left unset in the build.gradle file will use their default value, if any.
Table 1. List of site-wide attributes
Attribute name Description Default value

site-name

The name of the website. If set, the name will be displayed near the top of the page.

not set

site-githuburl

URL to the site’s repository on GitHub. Setting this will add a "View on GitHub" link in the navigation bar.

not set

site-seedu

Define this attribute if the project is an official SE-EDU project. This will render the SE-EDU navigation bar at the top of the page, and add some SE-EDU-specific navigation items.

not set

4.5. Per-file Documentation Settings

Each .adoc file may also specify some file-specific asciidoc attributes which affects how the file is rendered.

Asciidoctor’s built-in attributes may be specified and used as well.

Attributes left unset in .adoc files will use their default value, if any.
Table 2. List of per-file attributes, excluding Asciidoctor’s built-in attributes
Attribute name Description Default value

site-section

Site section that the document belongs to. This will cause the associated item in the navigation bar to be highlighted. One of: UserGuide, DeveloperGuide, LearningOutcomes*, AboutUs, ContactUs

* Official SE-EDU projects only

not set

no-site-header

Set this attribute to remove the site navigation bar.

not set

4.6. Site Template

The files in docs/stylesheets are the CSS stylesheets of the site. You can modify them to change some properties of the site’s design.

The files in docs/templates controls the rendering of .adoc files into HTML5. These template files are written in a mixture of Ruby and Slim.

Modifying the template files in docs/templates requires some knowledge and experience with Ruby and Asciidoctor’s API. You should only modify them if you need greater control over the site’s layout than what stylesheets can provide. The SE-EDU team does not provide support for modified template files.

5. Testing

5.1. Running Tests

There are three ways to run tests.

The most reliable way to run tests is the 3rd one. The first two methods might fail some GUI tests due to platform/resolution-specific idiosyncrasies.

Method 1: Using IntelliJ JUnit test runner

  • To run all tests, right-click on the src/test/java folder and choose Run 'All Tests'

  • To run a subset of tests, you can right-click on a test package, test class, or a test and choose Run 'ABC'

Method 2: Using Gradle

  • Open a console and run the command gradlew clean allTests (Mac/Linux: ./gradlew clean allTests)

See UsingGradle.adoc for more info on how to run tests using Gradle.

Method 3: Using Gradle (headless)

Thanks to the TestFX library we use, our GUI tests can be run in the headless mode. In the headless mode, GUI tests do not show up on the screen. That means the developer can do other things on the Computer while the tests are running.

To run tests in headless mode, open a console and run the command gradlew clean headless allTests (Mac/Linux: ./gradlew clean headless allTests)

5.2. Types of tests

We have two types of tests:

  1. GUI Tests - These are tests involving the GUI. They include,

    1. System Tests that test the entire App by simulating user actions on the GUI. These are in the systemtests package.

    2. Unit tests that test the individual components. These are in seedu.address.ui package.

  2. Non-GUI Tests - These are tests not involving the GUI. They include,

    1. Unit tests targeting the lowest level methods/classes.
      e.g. seedu.address.commons.StringUtilTest

    2. Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
      e.g. seedu.address.storage.StorageManagerTest

    3. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
      e.g. seedu.address.logic.LogicManagerTest

5.3. Troubleshooting Testing

Problem: HelpWindowTest fails with a NullPointerException.

  • Reason: One of its dependencies, HelpWindow.html in src/main/resources/docs is missing.

  • Solution: Execute Gradle task processResources.

6. Dev Ops

6.1. Build Automation

See UsingGradle.adoc to learn how to use Gradle for build automation.

6.2. Continuous Integration

We use Travis CI and AppVeyor to perform Continuous Integration on our projects. See UsingTravis.adoc and UsingAppVeyor.adoc for more details.

6.3. Coverage Reporting

We use Coveralls to track the code coverage of our projects. See UsingCoveralls.adoc for more details.

6.4. Documentation Previews

When a pull request has changes to asciidoc files, you can use Netlify to see a preview of how the HTML version of those asciidoc files will look like when the pull request is merged. See UsingNetlify.adoc for more details.

6.5. Making a Release

Here are the steps to create a new release.

  1. Update the version number in MainApp.java.

  2. Generate a JAR file using Gradle.

  3. Tag the repo with the version number. e.g. v0.1

  4. Create a new release using GitHub and upload the JAR file you created.

6.6. Managing Dependencies

A project often depends on third-party libraries. For example, Address Book depends on the Jackson library for XML parsing. Managing these dependencies can be automated using Gradle. For example, Gradle can download the dependencies automatically, which is better than these alternatives.
a. Include those libraries in the repo (this bloats the repo size)
b. Require developers to download those libraries manually (this creates extra work for developers)

Appendix A: Suggested Programming Tasks to Get Started

Suggested path for new programmers:

  1. First, add small local-impact (i.e. the impact of the change does not go beyond the component) enhancements to one component at a time. Some suggestions are given in Section A.1, “Improving each component”.

  2. Next, add a feature that touches multiple components to learn how to implement an end-to-end feature across all components. Section A.2, “Creating a new command: remark explains how to go about adding such a feature.

A.1. Improving each component

Each individual exercise in this section is component-based (i.e. you would not need to modify the other components to get it to work).

Logic component

Scenario: You are in charge of logic. During dog-fooding, your team realize that it is troublesome for the user to type the whole command in order to execute a command. Your team devise some strategies to help cut down the amount of typing necessary, and one of the suggestions was to implement aliases for the command words. Your job is to implement such aliases.

Do take a look at Section 2.3, “Logic component” before attempting to modify the Logic component.
  1. Add a shorthand equivalent alias for each of the individual commands. For example, besides typing clear, the user can also type c to remove all persons in the list.

    • Hints

    • Solution

      • Modify the switch statement in AddressBookParser#parseCommand(String) such that both the proper command word and alias can be used to execute the same intended command.

      • Add new tests for each of the aliases that you have added.

      • Update the user guide to document the new aliases.

      • See this PR for the full solution.

Model component

Scenario: You are in charge of model. One day, the logic-in-charge approaches you for help. He wants to implement a command such that the user is able to remove a particular tag from everyone in the address book, but the model API does not support such a functionality at the moment. Your job is to implement an API method, so that your teammate can use your API to implement his command.

Do take a look at Section 2.4, “Model component” before attempting to modify the Model component.
  1. Add a removeTag(Tag) method. The specified tag will be removed from everyone in the address book.

    • Hints

      • The Model and the AddressBook API need to be updated.

      • Think about how you can use SLAP to design the method. Where should we place the main logic of deleting tags?

      • Find out which of the existing API methods in AddressBook and Person classes can be used to implement the tag removal logic. AddressBook allows you to update a person, and Person allows you to update the tags.

    • Solution

      • Implement a removeTag(Tag) method in AddressBook. Loop through each person, and remove the tag from each person.

      • Add a new API method deleteTag(Tag) in ModelManager. Your ModelManager should call AddressBook#removeTag(Tag).

      • Add new tests for each of the new public methods that you have added.

      • See this PR for the full solution.

Ui component

Scenario: You are in charge of ui. During a beta testing session, your team is observing how the users use your address book application. You realize that one of the users occasionally tries to delete non-existent tags from a contact, because the tags all look the same visually, and the user got confused. Another user made a typing mistake in his command, but did not realize he had done so because the error message wasn’t prominent enough. A third user keeps scrolling down the list, because he keeps forgetting the index of the last person in the list. Your job is to implement improvements to the UI to solve all these problems.

Do take a look at Section 2.2, “UI component” before attempting to modify the UI component.
  1. Use different colors for different tags inside person cards. For example, friends tags can be all in brown, and colleagues tags can be all in yellow.

    Before

    getting started ui tag before

    After

    getting started ui tag after
    • Hints

      • The tag labels are created inside the PersonCard constructor (new Label(tag.tagName)). JavaFX’s Label class allows you to modify the style of each Label, such as changing its color.

      • Use the .css attribute -fx-background-color to add a color.

      • You may wish to modify DarkTheme.css to include some pre-defined colors using css, especially if you have experience with web-based css.

    • Solution

      • You can modify the existing test methods for PersonCard 's to include testing the tag’s color as well.

      • See this PR for the full solution.

        • The PR uses the hash code of the tag names to generate a color. This is deliberately designed to ensure consistent colors each time the application runs. You may wish to expand on this design to include additional features, such as allowing users to set their own tag colors, and directly saving the colors to storage, so that tags retain their colors even if the hash code algorithm changes.

  2. Modify NewResultAvailableEvent such that ResultDisplay can show a different style on error (currently it shows the same regardless of errors).

    Before

    getting started ui result before

    After

    getting started ui result after
  3. Modify the StatusBarFooter to show the total number of people in the address book.

    Before

    getting started ui status before

    After

    getting started ui status after
    • Hints

      • StatusBarFooter.fxml will need a new StatusBar. Be sure to set the GridPane.columnIndex properly for each StatusBar to avoid misalignment!

      • StatusBarFooter needs to initialize the status bar on application start, and to update it accordingly whenever the address book is updated.

    • Solution

Storage component

Scenario: You are in charge of storage. For your next project milestone, your team plans to implement a new feature of saving the address book to the cloud. However, the current implementation of the application constantly saves the address book after the execution of each command, which is not ideal if the user is working on limited internet connection. Your team decided that the application should instead save the changes to a temporary local backup file first, and only upload to the cloud after the user closes the application. Your job is to implement a backup API for the address book storage.

Do take a look at Section 2.5, “Storage component” before attempting to modify the Storage component.
  1. Add a new method backupAddressBook(ReadOnlyAddressBook), so that the address book can be saved in a fixed temporary location.

Session component

Scenario: You are in charge of session, whereby it is used to manage users logging in/out. Currently, there is a allPersonsHashMap variable that stores the data of all employees of the company in the app. This improves the efficiency of logging into the application with a time complexity of O(1). One issue with this current implementation right now is that whenever one’s particulars are updated, updatePersonHashMap will be called as well, which re-uploads every single enployee into the variable, which is not efficient in this case, with a time complexity of O(N).

Hence, your task is to modify the updatePersonHashMap such that whenever one’s particulars is edited, only the affected person object will be re-uploaded to the allPersonsHashMap variable, hence improving the efficiency.

A.2. Creating a new command: remark

By creating this command, you will get a chance to learn how to implement a feature end-to-end, touching all major components of the app.

Scenario: You are a software maintainer for addressbook, as the former developer team has moved on to new projects. The current users of your application have a list of new feature requests that they hope the software will eventually have. The most popular request is to allow adding additional comments/notes about a particular contact, by providing a flexible remark field for each contact, rather than relying on tags alone. After designing the specification for the remark command, you are convinced that this feature is worth implementing. Your job is to implement the remark command.

A.2.1. Description

Edits the remark for a person specified in the INDEX.
Format: remark INDEX r/[REMARK]

Examples:

  • remark 1 r/Likes to drink coffee.
    Edits the remark for the first person to Likes to drink coffee.

  • remark 1 r/
    Removes the remark for the first person.

A.2.2. Step-by-step Instructions

[Step 1] Logic: Teach the app to accept 'remark' which does nothing

Let’s start by teaching the application how to parse a remark command. We will add the logic of remark later.

Main:

  1. Add a RemarkCommand that extends Command. Upon execution, it should just throw an Exception.

  2. Modify AddressBookParser to accept a RemarkCommand.

Tests:

  1. Add RemarkCommandTest that tests that execute() throws an Exception.

  2. Add new test method to AddressBookParserTest, which tests that typing "remark" returns an instance of RemarkCommand.

[Step 2] Logic: Teach the app to accept 'remark' arguments

Let’s teach the application to parse arguments that our remark command will accept. E.g. 1 r/Likes to drink coffee.

Main:

  1. Modify RemarkCommand to take in an Index and String and print those two parameters as the error message.

  2. Add RemarkCommandParser that knows how to parse two arguments, one index and one with prefix 'r/'.

  3. Modify AddressBookParser to use the newly implemented RemarkCommandParser.

Tests:

  1. Modify RemarkCommandTest to test the RemarkCommand#equals() method.

  2. Add RemarkCommandParserTest that tests different boundary values for RemarkCommandParser.

  3. Modify AddressBookParserTest to test that the correct command is generated according to the user input.

[Step 3] Ui: Add a placeholder for remark in PersonCard

Let’s add a placeholder on all our PersonCard s to display a remark for each person later.

Main:

  1. Add a Label with any random text inside PersonListCard.fxml.

  2. Add FXML annotation in PersonCard to tie the variable to the actual label.

Tests:

  1. Modify PersonCardHandle so that future tests can read the contents of the remark label.

[Step 4] Model: Add Remark class

We have to properly encapsulate the remark in our Person class. Instead of just using a String, let’s follow the conventional class structure that the codebase already uses by adding a Remark class.

Main:

  1. Add Remark to model component (you can copy from Address, remove the regex and change the names accordingly).

  2. Modify RemarkCommand to now take in a Remark instead of a String.

Tests:

  1. Add test for Remark, to test the Remark#equals() method.

[Step 5] Model: Modify Person to support a Remark field

Now we have the Remark class, we need to actually use it inside Person.

Main:

  1. Add getRemark() in Person.

  2. You may assume that the user will not be able to use the add and edit commands to modify the remarks field (i.e. the person will be created without a remark).

  3. Modify SampleDataUtil to add remarks for the sample data (delete your addressBook.xml so that the application will load the sample data when you launch it.)

[Step 6] Storage: Add Remark field to XmlAdaptedPerson class

We now have Remark s for Person s, but they will be gone when we exit the application. Let’s modify XmlAdaptedPerson to include a Remark field so that it will be saved.

Main:

  1. Add a new Xml field for Remark.

Tests:

  1. Fix invalidAndValidPersonAddressBook.xml, typicalPersonsAddressBook.xml, validAddressBook.xml etc., such that the XML tests will not fail due to a missing <remark> element.

[Step 6b] Test: Add withRemark() for PersonBuilder

Since Person can now have a Remark, we should add a helper method to PersonBuilder, so that users are able to create remarks when building a Person.

Tests:

  1. Add a new method withRemark() for PersonBuilder. This method will create a new Remark for the person that it is currently building.

  2. Try and use the method on any sample Person in TypicalPersons.

[Step 7] Ui: Connect Remark field to PersonCard

Our remark label in PersonCard is still a placeholder. Let’s bring it to life by binding it with the actual remark field.

Main:

  1. Modify PersonCard's constructor to bind the Remark field to the Person 's remark.

Tests:

  1. Modify GuiTestAssert#assertCardDisplaysPerson(…​) so that it will compare the now-functioning remark label.

[Step 8] Logic: Implement RemarkCommand#execute() logic

We now have everything set up…​ but we still can’t modify the remarks. Let’s finish it up by adding in actual logic for our remark command.

Main:

  1. Replace the logic in RemarkCommand#execute() (that currently just throws an Exception), with the actual logic to modify the remarks of a person.

Tests:

  1. Update RemarkCommandTest to test that the execute() logic works.

A.2.3. Full Solution

See this PR for the step-by-step solution.

Appendix B: Product Scope

Target user profile:

  • has a need to manage a significant number of contacts

  • prefer desktop apps over other types

  • can type fast

  • prefers typing over mouse input

  • is reasonably comfortable using CLI apps

Value proposition: manage contacts faster than a typical mouse/GUI driven app

Appendix C: User Stories

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

Priority As a …​ I want to …​ So that I can…​

* * *

User

Access my payroll

Calculate my incoming pay according to the number of hours I have worked easily

* * *

User

Check my schedule

Know where I am deployed for the day

* * *

User

Check my notifications via CLI

View my notifications

* * *

Elevated user

Set employees’ access levels

Ensure that employees have the appropriate level of access to classified items/areas

* * *

Elevated user

Add/Delete/Edit employees into the database

Ensure that the company manpower list is updated

* * *

Elevated user

Set schedule for employee

Deploy employees to their designated location.

* * *

User

Edit my personal particulars

Update them as required (Edit function)

* * *

Elevated user

Access employees leave applications

Approve/Reject leaves for employees

* * *

User

Search for a specific contact

Obtain the particulars of the contact easily

* * *

User

List the manpower for the day

Allocate my manpower accordingly

* * *

User

Sort my address book by name and department

Look for contacts that I want easily

* * *

User

List out the departments that are available

Know what departments I can filter

* * *

User

Filter out and view my relevant colleague’s department

Spend less time searching for their contact details and contact them

* * *

Elevated User

Change the department of the employees

They are allocated at the correct department

* * *

User

Set password to my address book

Prevent unauthorized access to my address book

* * *

User

Apply for leave application

I can go on leave

* * *

User

Filter out leave application by NRIC

I can list out leave applications under me

* * *

User

I have access to my leave applications

I can delete them

* * *

User

I can list out all leave applications

I can gauge which day for me to apply leave without the company lacking of manpower

* * *

Elevated User

I have access to all employee’s leave applications

I can delete them

* * *

User

Check in and out during my work days

I will be able to view the number of hours I have worked for

* * *

Elevated user

Check my employees’ working hours

Pay them accordingly

* * *

Administrator

Restrict employee access to the address book application

Perform maintenance or upgrades

* *

User

Check the staff’s direct superior

Feedback to the particular superior

* *

User

Set my availability status (available, busy, away)

Let colleagues decide their best course of action according to my status

* *

Elevated User

Send out alerts to an individual/group

Ensure that everyone is caught up with the latest developments

* *

User

Notified via email/sms

Get the latest updates at the earliest possible time

* *

User

Upload profile picture

I can be identified easily by others

* *

User

Use keyboard shortcuts

Quickly navigate around the address book

*

User

Deploy available cleaners

Get the place cleaned up as soon as possible.

Appendix D: Use Cases

(For all use cases below, the System is the AddressBook and the Actor is the user, unless specified otherwise)

Use case: Login to the application

MSS

  1. User types login command, followed by the appropriate parameters.

  2. AddressBook shows welcome message upon successful login.

    Use case ends.

Extensions

  • 2a. Format of login command is wrong.

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. Format of login command is correct, but invalid NRIC and/or password.

    • 2b1. AddressBook shows an error message.

      Use case ends.

  • 2c. Attempts to login when someone (whether the user himself or someone else) is already logged in.

    • 2c1. AddressBook shows an error message.

      Use case ends.

Use case: Check login status

MSS

  1. User enters command checkloginstatus.

  2. AddressBook shows whether the user is logged in or out.

    Use case ends.

Use case: Logout of the application

MSS

  1. User enters command logout.

  2. AddressBook logs the user out of the application and shows a success message.

    Use case ends.

Extensions

  • 2a. User is not logged in when executing that command.

    • 2a1. AddressBook shows an error message that the user is not logged in.

Use case: Edit personal particulars

MSS

  1. User enters edit command, followed by appropriate parameters.

  2. AddressBook shows a success message followed by the logged in person’s particulars.

    Use case ends.

Extensions

  • 2a. The edited format is wrong

    • 2a1. AddressBook shows an error message, followed by command usage guide.

      Use case ends.

  • 2b. User is not logged in

    • 2b1. AddressBook shows an error message.

      Use case ends.

Use case: Add Employee

MSS

  1. User enters add command, followed by the details of the person to be added.

  2. AddressBook shows a success message followed by the details of the newly added person.

    Use case ends.

Extensions

  • 2a. Person already exists (identical NRIC number) in the AddressBook.

    • 2a1. AddressBook shows person already exists message.

      Use case ends.

  • 2b. The added person has missing details.

    • 2b1. AddressBook shows an error message.

      Use case ends.

  • 2c. The added person has wrong entry format.

    • 2c1. AddressBook shows an error message.

      Use case ends.

  • 2d. User is not logged in prior to executing this command.

    • 2d1. AddressBook shows an error message.

      Use case ends.

  • 2e. User does not have sufficient priority level prior to executing this command.

    • 2e1. AddressBook shows an error message.

      Use case ends.

Use case: Delete Employee

MSS

  1. User enters delete command followed by the INDEX of the person in the AddressBook list to delete.

  2. AddressBook shows success message followed by the details of the person that has been deleted.

    Use case ends.

Extensions

  • 2a. User entered command in wrong format.

    • 2a1. AddressBook shows an error message followed by the command usage guide.

      Use case ends.

  • 2b. User is not logged in prior to executing this command.

    • 2b1. AddressBook shows an error message.

      Use case ends.

  • 2c. User does not have sufficient priority level prior to executing this command.

    • 2c1. AddressBook shows an error message.

      Use case ends.

  • 2d. User attempts to delete him/herself

    • 2d1. AddressBook shows an error message.

      Use case ends.

  • 2e. User entered a value INDEX that is out of range of the list of persons that is currently shown on the list of People tab.

    • 2e1. Address shows an error message.

      Use case ends.

Use case: Change priority level of user

MSS

  1. User enters setplvl command, followed by the appropriate parameters.

  2. AddressBook updates the priority level of the person at stated index.

    Use case ends.

Extensions

  • 2a. User is not logged in

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. User entered inappropriate parameters.

    • 2b1. AddressBook shows an error message with the details of the error.

      Use case ends.

  • 2c. User logged in, but insufficient priority level

    • 2c1. AddressBook shows an error message.

      Use case ends.

  • 2d. User logged in, sufficient priority level, but tries to edit his/her own priority level

    • 2d1. AddressBook shows an error message.

      Use case ends.

Use case: Change department of user

MSS

  1. User enters setdepartment command, followed by the appropriate parameters.

  2. AddressBook updates the department of the user at the stated index.

    Use case ends.

Extensions

  • 2a. User is not logged in.

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. User does not have the correct priority level.

    • 2b1. AddressBook shows an error message.

      Use case ends.

  • 2c. User enters invalid parameters.

    • 2c1. AddressBook shows an error message.

      Use case ends.

  • 2d. User tries to edit his/her own department.

    • 2d1. AddressBook shows an error message.

      Use case ends.

Use case: Apply leave application

MSS

  1. User enters leave command with the desired leave application date.

  2. AddressBook adds the leave application to the database for approval.

    Use case ends.

Extensions

  • 2a. User not logged in

    • 2a1. AddressBook shows an error message.

      User case ends.

  • 2b. The given date is an invalid calendar date.

    • 2b1. AddressBook shows an error message.

      Use case resumes to step 1.

  • 2c. The given date has passed.

    • 2c1. AddressBook shows an error message.

      Use case resumes to step 1.

  • 2d. Today’s date is given.

    • 2d1. AddressBook shows an error message.

      Use case resumes to step 1.

  • 2e. The given date is an existing leave application.

    • 2e1. AddressBook shows an error message.

      Use case resumes to step 1.

Use case: List leave application

MSS

  1. User enters listleave command.

  2. AddressBook list all leave applications.

    Use case ends.

Use case: Filter leave application

MSS

  1. User enters filterleave command with the desired NRIC he wants to filter.

  2. AddressBook list all leave applications under that NRIC.

    Use case ends.

Extensions

  • 2a. Leave application under the NRIC given does not exist

    • 2a1. AddressBook shows an error message.

      User case ends.

Use case: Delete leave application

MSS

  1. User enters deleteleave command with the index of the leave application he wants to delete.

  2. AddressBook deletes the leave application from the database.

    Use case ends.

Extensions

  • 2a. User not logged in

    • 2a1. AddressBook shows an error message.

      User case ends.

  • 2b. Invalid index given.

    • 2b1. AddressBook shows an error message.

      Use case resumes at step 1.

  • 2c. Not authorized to delete other user’s leave application due to insufficient priority level.

    • 2c1. AddressBook shows an error message.

      Use case ends.

Use case: Approve/reject leave application

MSS

  1. User enters approve / reject command with the index of the leave application he wants to approve.

  2. AddressBook approves the leave application.

    Use case ends.

Extensions

  • 2a. User not logged in

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. Invalid index given.

    • 2b1. AddressBook shows an error message.

      Use case resumes to step 1.

  • 2c. Not authorized to approve/reject your own leave application.

    • 2c1. AddressBook shows an error message.

      Use case ends.

  • 2d. Not authorized to approve/reject other user’s leave application due to insufficient priority level.

    • 2d1. AddressBook shows an error message.

      Use case ends.

  • 2e. The leave application was already been approved/rejected.

    • 2e1. AddressBook shows an error message.

      Use case ends.

Use case: Check schedule

MSS

  1. User enters schedule command followed by appropriate parameters.

  2. AddressBook display user’s schedule.

    Use case ends.

Extensions

  • 2a. User is not logged in

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. No user schedule found.

    • 2b1. Address book displays message showing there are no schedule allocated.

      Use case ends.

  • 2c. Inappropriate parameters.

    • 2c1. AddressBook shows an error message.

      Use case ends.

    Use case ends.

Use case: Set schedule

MSS

  1. User enters setschedule command followed by appropriate parameters.

  2. AddressBook sets the schedule of the target employee.

    Use case ends.

Extensions

  • 2a. User is not logged in

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. Target is not user and user is not ADMINISTRATOR priority level.

    • 2b1. AddressBook shows an error message.

      Use case ends.

  • 2c. Inappropriate parameters.

    • 2c1. AddressBook shows an error message.

      Use case ends.

Use case: Find person

MSS

  1. User keys in the keyword to search in find command.

  2. AddressBook displays the list of names which contains the keyword given.

    Use case ends.

Extensions

  • 2a. AddressBook does not contain the keyword entered.

    • 2a1. AddressBook displays entered keyword not found.

      Use case ends.

Use case: Sort contact

MSS

  1. User enters sort command, followed by the appropriate parameters.

  2. AddressBook shows a sorted list by the field and order.

    Use case ends.

Extensions

  • 2a. The given parameter is invalid.

    • 2a1. AddressBook shows an error message.

      Use case resumes to step 1.

Use case: List department

MSS

  1. User enters listdepartment command.

  2. AddressBook shows a list of available departments.

    Use case ends.

Use case: Filter department

MSS

  1. User enters filterdepartment command, followed by the department names to filter.

  2. AddressBook shows the list of filtered department.

    Use case ends.

Extensions

  • 2a. The keyword management is entered.

    • 2a1. AddressBook shows an error message.

      Use case resumes to step 1.

  • 2b. The keyword entered is not found.

    • 2b1. AddressBook shows an empty list.

      Use case ends.

Use case: Check in to work

MSS

  1. User enters check command followed by in mode to check in.

  2. AddressBook shows checked in message and updates checked in/out status of the employee.

Extension

  • 2a. User not logged in.

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. User enters invalid parameter.

    • 2b1. AddressBook shows an error message.

      Use case resumes to step 1.

  • 2c. User already checked in.

    • 2c1. AddressBook shows an error message.

      Use case resumes to step 1.

Use case: Check out from work

MSS

  1. User enters check command followed by out mode to check out.

  2. AddressBook shows checked out message and updates checked in/out status of the employee.

Extension

  • 2a. User not logged in.

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. User enters invalid parameter.

    • 2b1. AddressBook shows an error message.

      Use case resumes to step 1.

  • 2c. User already checked out.

    • 2c1. AddressBook shows an error message.

      Use case resumes to step 1.

Use case: Reset application

MSS

  1. User enters command reset.

  2. AddressBook deletes data/AddressBook.xml and data/leave.xml and exits the program.

    Use case ends.

Extensions

  • 2a. User logged in but is not holding a priority level of "I.T. UNIT".

    • 2a1. AddressBook shows an error message.

      Use case ends.

  • 2b. User is not logged in

    • 2b1. AddressBook shows an error message.

      Use case ends.

Appendix E: Non Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 9 or higher installed.

  2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.

  3. 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: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Private contact detail

A contact detail that is not meant to be shared with others

Appendix G: Instructions for Manual Testing

Given below are instructions to test the app manually. Read the following note and warning messages before proceeding to conduct the tests.

These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

The following prerequisites apply to ALL test cases: -

  1. The following test cases assume a default state when the app is first launched or reset, without any prior modifications to the employees list and its' corresponding accounts. Modify your test case according to whatever changes that were made prior to executing each individual test cases.

  2. The following test cases assume that the employee list is neither sorted nor filtered prior to executing the test cases. Modify your test cases accordingly if you have sorted or filtered the list.

G.1. Launch and Shutdown

  1. Initial launch

    1. Download the jar file and copy into an empty folder

    2. Double-click the jar file
      Expected: Shows the GUI with a set of sample employees and leave applications. The window size may not be optimum.

  2. Saving window preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.
      Expected: The most recent window size and location is retained.

G.2. Personal User Account

G.2.1. Login to the application

  1. Logs in to the application using a registered NRIC and associating password when not logged in prior to executing the command.

    1. Prerequisites: -

      1. Login NRIC has to be registered in the application, either through the sample data or added manually by an administrator.

      2. You are not logged in prior to testing this function.

    2. Test case: login ic/S1234567E pwd/Password
      Expected: Status message box states that you are logged in successfully, with your name, and your schedule (if any). Status bar will now show that you are logged in with the corresponding name of the user who logged in.

    3. Test case: login ic/S12345678 pwd/Password
      Expected: Not logged in. Format of NRIC is incorrect and Status message box shows the NRIC constraints. Status bar remains unchanged.

    4. Test case: login ic/S1234567E pwd/high-five
      Expected: Not logged in. Password format is incorrect and Status message box shows password constraints. Status bar remains unchanged.

    5. Test case: login ic/S1234567E
      Expected: Not logged in. Command format is invalid and Status message box shows the command usage. Status bar remains unchanged.

    6. Test case: login ic/S1234567E pwd/SomeRandomPwd
      Expected: Not logged in. Password does not match the one that’s registered with the app; Status message box shows error. Status bar remains unchanged.

  2. Logs in to the application using a registered NRIC and associating password when already logged in prior to executing the command.

    1. Prerequisites: You are already logged in prior to testing this function.

    2. Test case: login ic/T1234567E pwd/Password
      Expected: Status message box shows the message: -
      You are already logged in. Logout first before logging in again.
      Login status remain unchanged; status bar remain unchanged.

G.2.2. Checks the login status of the application

  1. Checks the login status of this app. For this case, someone is logged in to the app.

    1. Prerequisites: Someone is logged in to the app.

    2. Test case: checkloginstatus
      Expected: Status message box shows the NRIC of the user who is logged in to the app.

  2. Checks the login status of this app. For this case, user is not logged in prior to executing this command.

    1. Prerequisites: User is not logged in prior to executing this command.

    2. Test case: checkloginstatus
      Expected: Status message box shows that you are not logged in.

G.2.3. Logout of the application

  1. Logs out of the application.

    1. Prerequisites: User is logged into the app prior to executing this command.

    2. Test case: logout
      Expected: Status message box shows that you have successfully logged out. Status bar footer now shows: Not logged in.

G.2.4. Editing personal particulars of the person who is logged in

Refer to EditCommand section of the User Guide for the list of editable parameters.

  1. Edits the editable particulars of the person who is logged in.

    1. Prerequisites: User must be logged in to the application.

    2. Test case: edit n/Stacy Mak e/stacym@banksy.com
      Expected: Particular of the logged in person gets updated with name of Stacy Mak and email of stacym@banksy.com. Status message box shows the details of the updated person. Status bar footer now shows that the new name of the person who is logged in.

    3. Test case: edit a/Yishun, Singapore p/91234567
      Expected: Particular of the logged in person gets updated with address now at Yishun, Singapore, and phone number of 91234567. Status message box shows the details of the updated person. Status bar footer remains unchanged.

    4. Test case: edit p/string p/96289700
      Expected: Particular of the logged in person gets updated with phone number of 96289700. The previous invalid phone input of string gets ignored as it is superseded with the valid phone input of 96289700. Status message box shows the details of the updated person. Status bar footer remains unchanged.

    5. Test case: edit p/96289700 p/string
      Expected: Details of the person not edited. Status message box shows error message with the constraints of a phone number. The phone input of 96289700 gets ignored as it is superseded by the phone input of string, and hence led to the error message.

G.3. Employee Management Database

G.3.1. Adding an employee

  1. Adding an employee in to the address book.

    1. Prerequisites: User must be logged in as an administrator.

    2. Test case: add n/Carol Crowe ic/S1947829E pwd/Password p/94724372 e/carol@abbank-sg.com d/Junior Management a/Block 123 Jurong Street 123, #12-345 wr/7.5
      Expected: Carol Crowe is added in to the list. Details of the added contact is shown in the status message.

    3. Test case: add n/Carol Crowe ic/F1234567E pwd/Password p/94724372 e/carol@abbank-sg.com d/Junior Management a/Block 123 Jurong Street 123, #12-345 wr/7.5
      Expected: No employee is added. The NRIC of Carol Crowe is the same as Charlotte Oliveiro. Error detail shown in the status message.

G.3.2. Deleting an employee

  1. Deleting an employee while all employees are listed.

    1. Prerequisites: -

      1. List all employees using the list command. Multiple employees in the list.

      2. User must be logged in as an administrator.

    2. Test case: delete 1
      Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.

    3. Test case: delete 0
      Expected: No employee is deleted. Error details shown in the status message. Status bar remains the same.

    4. Other incorrect delete commands to try: delete, delete x (where x is larger than the list size)
      Expected: Similar to previous.

G.3.3. Change priority level of employee

Refer to Appendix B of User Guide for the list of priority level.

  1. Change the priority level of an employee with one logged in as ADMINISTRATOR.

    1. Prerequisites: -

      1. You must be logged in as an administrator.

      2. The following test cases assume that you are logged in as Alex Yeoh, an administrator with NRIC of S1234567E and password of Password. Modify the test cases accordingly if you have made any modifications to the employee list prior to executing the following test cases.

    2. Test case: setplvl 1 plvl/3
      Expected: Priority level of the employee at index 1 gets updated to a priority level of BASIC. Status message box shows that the priority level of the name of employee at index 1 has been changed to BASIC.

    3. Test case: Attempting to change your own priority level setplvl 2 plvl/3
      Expected: Priority level of the logged in user does not get changed. Status message box shows error that you are not allowed to update your own priority level.

    4. Test case: setplvl 1 plvl/999
      Expected: Priority level of the employee at index 1 is not updated. Input priority level is out of the range of valid priority level values. Status message bar shows error message.

  2. Attempt to change the priority level of an employee with one logged in without admin rights.

    1. Prerequisites: You must be logged in with an account that is not of ADMINISTRATOR priority level.

    2. Test case: setplvl 2 plvl/3
      Expected: Priority level of the employee at index 2 is not updated. The user who is attempting to change the priority level does not have the rights to do so. Status message box shows an error message.

G.3.4. Change the department of an employee

  1. Change the department of an employee from the given index.

    1. Prerequisites: Have to be logged in as an administrator and have multiple employees in the list.

    2. Test case: setdepartment 1 d/Top Management
      Expected: The employee’s department at index 1 will be changed to Top Management. Details of the employee name and the changed department name will be shown in the status message. If the employee at index 1 is the logged in employee himself/herself, the department will not be changed. Error details is shown in the status message.

    3. Test case: setdepartment 1 d/Top Department
      Expected: Department name is of the wrong format. Error details is shown in the status message.

    4. Test case: setdepartment 0 d/Top Management
      Expected: No employee at index 0. Error details is shown in the status message.

G.4. Leave Application

G.4.1. Leave application

  1. Requesting leave application

    1. Prerequisites: User must be logged in.

    2. Test case: leave date/01/01/2019
      Expected: Leave application requested. Details of the leave application will be reflected in the leave panel.

    3. Test case: leave date/01/02/2018
      Expected: No leave application created as the given date has passed. Error details shown in the status message.

    4. Test case: leave date/29/02/2019
      Expected: No leave application created as invalid calendar date is given. Error details shown in the status message.

G.4.2. Filter leave application

  1. Filter leave application

    1. Test case: filterleave S8570520Q
      Expected: List all leave application under the user with NRIC, S8570520Q.

    2. Test case: filterleave S1212121A , filterleave S1313131A
      Expected: 0 leaves listed. This is due to no such leave application under S1212121A and S1313131A.

    3. If NRIC is not valid such as: filterleave S8570520, filterleave 8570520Q, filterleave 8570520
      Expected: 0 leaves listed. This is due to invalid NRIC.

G.4.3. Delete leave application

  1. Delete leave application

    1. Prerequisites: User must be logged in as Administrator. List all leave applications using the listleave command. Multiple leave applications in the list.

    2. Test case: deleteleave 1 where 1 is the index of the leave application under the list
      Expected: First leave application is deleted from the list. Details of the deleted leave application shown in the status message.

    3. Test case: deleteleave 0
      Expected: No leave is deleted. Error details shown in the status message.

    4. Other incorrect deleteleave commands to try: deleteleave, deleteleave x (where x is larger than the list leave size)
      Expected: Similar to previous.

G.4.4. Approve leave application

  1. Approve leave application

    1. Prerequisites: User must be logged in as Administrator. List all leave applications using the listleave command. Multiple leave applications in the list. Leave application under index 1 is not requested by the logged in user.

    2. Test case: approve 1 where 1 is the index of the leave application under the list.
      Expected: Leave application approved. Details of the approved leave application shown in the status message.

    3. Test case: approve 0
      Expected: No leave application is approved. Error details shown in the status message.

    4. Other incorrect approve commands to try: approve, approve x (where x is larger than the list leave size)
      Expected: Similar to previous.

G.4.5. Reject leave application

  1. Reject leave application

    1. Prerequisites: User must be logged in as Administrator. List all leave applications using the listleave command. Multiple leave applications in the list. Leave application under index 1 is not requested by the logged in user.

    2. Test case: reject 1 where 1 is the index of the leave application under the list.
      Expected: Leave application rejected. Details of the rejected leave application shown in the status message.

    3. Test case: reject 0
      Expected: No leave application is rejected. Error details shown in the status message.

    4. Other incorrect reject commands to try: reject, reject x (where x is larger than the list leave size)
      Expected: Similar to previous.

G.5. Scheduling

G.5.1. List schedule

  1. Lists the schedule of an employee in address book as a non-administrator.

    1. Prerequisites: Logged in as any non-administrator user and is listed at index 4.

    2. Test case: schedule 4
      Expected: Displays own schedule. Details of schedule shown in status message.

    3. Test case: schedule 3
      Expected: User has invalid priority level. Error details shown in status message.

    4. Test case: schedule 0
      Expected: Index 0 is invalid. Error details shown in status message.

  2. Lists the schedule of an employee in address book as an administrator.

    1. Prerequisites: Logged in as any administrator and is listed at index 1.

    2. Test case: schedule 1
      Expected: Displays own schedule. Details of schedule shown in status message.

    3. Test case: schedule 3
      Expected: Displays schedule of employee at index 3. Details of schedule shown in status message.

    4. Test case: schedule 0
      Expected: Index 0 is invalid. Error details shown in status message.

G.5.2. Set schedule

  1. Sets the schedule of an employee in address book as a non-administrator.

    1. Prerequisites: Logged in as any non-administrator user and is listed at index 4.

    2. Test case: setschedule 4 ts/0900 te/1700 v/Level 5
      Expected: Successfully set own schedule. Message is shown in the status message.

    3. Test case: setschedule 3 ts/0900 te/1700 v/Level 5
      Expected: User has invalid priority level. Error detail shown in the status message.

    4. Test case: setschedule 4 ts/0900 te/0900 v/Level 5
      Expected: Time start and time end are the same. Error detail shown in the status message.

  2. Sets the schedule of an employee in address book as an administrator.

    1. Prerequisites: Logged in as any administrator and is listed at index 1.

    2. Test case: setschedule 1 ts/0900 te/1700 v/Level 5
      Expected: Successfully set own schedule. Message is shown in the status message.

    3. Test case: setschedule 3 ts/0900 te/1700 v/Level 5
      Expected: Successfully set schedule for employee at index 3. Message is shown in the status message.

    4. Test case: setschedule 4 ts/0900 te/0900 v/Level 5
      Expected: Time start and time end are the same. Error detail shown in the status message.

G.6. Filtering/Sorting

G.6.1. Locating employees by name

  1. Finds the employees whose name contains any of the given keywords.

    1. Test case: find alex
      Expected: Employees whose name matches with alex is listed. Details of the number of employees listed is shown in the status message.

    2. Test case: find alex bernice
      Expected: Employees whose name matches with alex or bernice is listed. Details of the number of employees listed is shown in the status message.

    3. Test case: find
      Expected: Invalid command format. Error details is shown in the status message.

G.6.2. Sort address book contacts

  1. Sorts the name list either in ascending or descending order.

    1. Prerequisites: List all employees using the list command. Multiple employees in the list.

    2. Test case: sort name asc
      Expected: The list will be sorted in ascending order by names. Details of the address book has been sorted is shown in the status message.

    3. Test case: sort department desc
      Expected: The list will be sorted in descending order by department names. Details of the address book has been sorted is shown in the status message.

    4. Test case: sort invalid asc
      Expected: Invalid command format. Error details is shown in the status message.

    5. Test case: sort name invalid
      Expected: Invalid command format. Error details is shown in the status message.

G.6.3. Filtering department

  1. Filters the list with employees who are in the department from the provided keywords.

    1. Test case: filterdepartment junior
      Expected: Employees whose department matches with junior is listed. Details of the number of employees listed is shown in the status message.

    2. Test case: filterdepartment junior senior
      Expected: Employees whose department matches with junior or senior is listed. Details of the number of employees listed is shown in the status message.

    3. Test case: filterdepartment
      Expected: Invalid command format. Error details is shown in the status message.

G.7. Miscellaneous

G.7.1. Selecting an employee

  1. Selects an employee while all employee are listed.

    1. Prerequisites: List all employees using the list command. Multiple employees in the list.

    2. Test case: select 1
      Expected: First contact is selected from the list. Details of the index of the selected employee is shown in the status message.

    3. Test case: select 0
      Expected: No employee is selected. Error details shown in the status message.

    4. Other incorrect select commands to try: select, select x (where x is larger than the list size)
      Expected: Similar to previous.

G.7.2. Check in/out working hours

  1. Checking in or out for a person.

    1. Prerequisites: Log in for the person using the login command.

    2. Test case: check m/in
      Expected: The person has checked in to work. Details of the checked in person shown in the status message. The checked in/out status is updated.

    3. Test case: check m/IN
      Expected: No person checked in. Error details shown in the status message. Checked in/out status remains the same.

    4. Test case: check m/10
      Expected: No person checked in. Error details shown in the status message. Checked in/out status remains the same.

    5. Other incorrect check commands to try: check, check in, check mode/in
      Expected: Similar to previous.

G.7.3. Reset the application

  1. Resets the application, by deleting data/AddressBook.xml and data/leave.xml, provided that any of the files exist.

    1. Prerequisites: -

      1. User must be logged in with a priority level of I.T. UNIT.

      2. In order for the test to be efficient, you may ensure that the files data/AddressBook.xml and data/leave.xml exist in the folder data.

    2. Test case: type command reset
      Expected: Application will exit. The two files will be deleted upon application exit. When you manually re-open the app, a sample list of employee and leave will be shown.