diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 6ff220b5196..96d80563736 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -43,3 +43,5 @@ jobs: uses: codecov/codecov-action@v3 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + diff --git a/build.gradle b/build.gradle index a2951cc709e..997495b6ade 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,7 @@ task coverage(type: JacocoReport) { dependencies { String jUnitVersion = '5.4.0' String javaFxVersion = '17.0.7' + String mockitoVersion = '5.11.0' implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' @@ -61,8 +62,12 @@ dependencies { implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: jUnitVersion - testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion + + testImplementation "org.mockito:mockito-core:$mockitoVersion" + + testImplementation 'org.testfx:testfx-junit5:4.0.16-alpha' + testImplementation 'org.testfx:testfx-core:4.0.16-alpha' } shadowJar { diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index bee758078f0..321aee8712c 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -46,4 +46,5 @@ public interface Logic { * Set the user prefs' GUI settings. */ void setGuiSettings(GuiSettings guiSettings); + boolean isInitialModuleListPanelDisplayed(); } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 5aa3b91c7d0..88030fea5f0 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -85,4 +85,10 @@ public GuiSettings getGuiSettings() { public void setGuiSettings(GuiSettings guiSettings) { model.setGuiSettings(guiSettings); } + + @Override + public boolean isInitialModuleListPanelDisplayed() { + // Check if there are any modules in the address book + return !model.getAddressBook().getModuleList().isEmpty(); + } } diff --git a/src/main/java/seedu/address/logic/parser/AddClassCommandParser.java b/src/main/java/seedu/address/logic/parser/AddClassCommandParser.java index cc2262b06f9..3ff3b8ca12c 100644 --- a/src/main/java/seedu/address/logic/parser/AddClassCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddClassCommandParser.java @@ -28,7 +28,7 @@ public AddClassCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_MODULECODE, PREFIX_TUTORIALCLASS); if (!arePrefixesPresent(argMultimap, PREFIX_MODULECODE, PREFIX_TUTORIALCLASS) - || !argMultimap.getPreamble().isEmpty()) { + || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddClassCommand.MESSAGE_USAGE)); } diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index 1cf2ba9487e..dd520856e22 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; +import javafx.collections.FXCollections; import javafx.collections.ObservableList; import seedu.address.commons.util.ToStringBuilder; import seedu.address.model.module.ModuleCode; @@ -52,6 +53,11 @@ public void setPersons(List persons) { this.persons.setPersons(persons); } + public void setModules(List modules) { + requireNonNull(modules); + this.modules.clear(); + this.modules.addAll(modules); + } /** * Resets the existing data of this {@code AddressBook} with {@code newData}. */ @@ -59,6 +65,7 @@ public void resetData(ReadOnlyAddressBook newData) { requireNonNull(newData); setPersons(newData.getPersonList()); + setModules(newData.getModuleList()); } //// person-level operations @@ -82,6 +89,7 @@ public void addPerson(Person p) { /** * Returns true if a module with the same identity as {@code module} exists in the address book. */ + @Override public boolean hasModule(ModuleCode module) { requireNonNull(module); return modules.contains(module); @@ -106,6 +114,7 @@ public ModuleCode findModuleFromList(ModuleCode module) { * Adds a module to the address book. * The module must not already exist in the address book. (TODO) */ + @Override public void addModule(ModuleCode m) { modules.add(m); } @@ -143,8 +152,9 @@ public ObservableList getPersonList() { return persons.asUnmodifiableObservableList(); } - public List getModuleList() { - return modules; + @Override + public ObservableList getModuleList() { + return FXCollections.observableList(modules); } @Override diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index 366fccca875..4e023b3e8f3 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -14,6 +14,8 @@ public interface Model { /** {@code Predicate} that always evaluate to true */ Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + Predicate PREDICATE_SHOW_ALL_MODULES = unused -> true; + /** * Replaces user prefs data with the data in {@code userPrefs}. @@ -57,12 +59,6 @@ public interface Model { * Returns true if a person with the same identity as {@code person} exists in the address book. */ boolean hasPerson(Person person); - - /** - * Returns true if a module with the same identity as {@code module} exists in the address book. - */ - boolean hasModule(ModuleCode module); - /** * Finds the module object from the list if it exists. Else, returns null. * @@ -98,15 +94,17 @@ public interface Model { /** Returns an unmodifiable view of the filtered person list */ ObservableList getFilteredPersonList(); + ObservableList getFilteredModuleList(); /** * Updates the filter of the filtered person list to filter by the given {@code predicate}. * @throws NullPointerException if {@code predicate} is null. */ void updateFilteredPersonList(Predicate predicate); - + void updateFilteredModuleList(Predicate predicate); /** * Search for person by a given {@code predicate}. */ Person searchPersonByPredicate(Predicate predicate); + } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index c49f7a25dcf..abc0bc8823e 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -24,6 +24,7 @@ public class ModelManager implements Model { private final AddressBook addressBook; private final UserPrefs userPrefs; private final FilteredList filteredPersons; + private final FilteredList filteredModules; /** * Initializes a ModelManager with the given addressBook and userPrefs. @@ -36,6 +37,7 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs this.addressBook = new AddressBook(addressBook); this.userPrefs = new UserPrefs(userPrefs); filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + filteredModules = new FilteredList<>(this.addressBook.getModuleList()); } public ModelManager() { @@ -94,13 +96,6 @@ public boolean hasPerson(Person person) { requireNonNull(person); return addressBook.hasPerson(person); } - - @Override - public boolean hasModule(ModuleCode module) { - requireNonNull(module); - return addressBook.hasModule(module); - } - @Override public ModuleCode findModuleFromList(ModuleCode module) { requireNonNull(module); @@ -140,6 +135,11 @@ public ObservableList getFilteredPersonList() { return filteredPersons; } + @Override + public ObservableList getFilteredModuleList() { + return filteredModules; + } + @Override public void updateFilteredPersonList(Predicate predicate) { requireNonNull(predicate); @@ -147,6 +147,17 @@ public void updateFilteredPersonList(Predicate predicate) { } @Override + public void updateFilteredModuleList(Predicate predicate) { + requireNonNull(predicate); + filteredModules.setPredicate(predicate); + } + /** + * Searches for a person in the list of filtered persons based on the given predicate. + * + * @param predicate The predicate used to filter persons. + * @return The first person that matches the predicate, or {@code null} if no person matches. + * @throws NullPointerException if the predicate is {@code null}. + */ public Person searchPersonByPredicate(Predicate predicate) { requireNonNull(predicate); return filteredPersons.stream().filter(predicate).findFirst().orElse(null); @@ -168,5 +179,4 @@ public boolean equals(Object other) { && userPrefs.equals(otherModelManager.userPrefs) && filteredPersons.equals(otherModelManager.filteredPersons); } - } diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java index d1397db1fa6..1b7eb6e50cb 100644 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java @@ -1,7 +1,5 @@ package seedu.address.model; -import java.util.List; - import javafx.collections.ObservableList; import seedu.address.model.module.ModuleCode; import seedu.address.model.person.Person; @@ -17,5 +15,7 @@ public interface ReadOnlyAddressBook { */ ObservableList getPersonList(); - List getModuleList(); + ObservableList getModuleList(); + boolean hasModule(ModuleCode moduleCode); + void addModule(ModuleCode moduleCode); } diff --git a/src/main/java/seedu/address/model/module/ModuleCode.java b/src/main/java/seedu/address/model/module/ModuleCode.java index 3506db8a8de..ebdad1d4faa 100644 --- a/src/main/java/seedu/address/model/module/ModuleCode.java +++ b/src/main/java/seedu/address/model/module/ModuleCode.java @@ -128,6 +128,7 @@ public boolean hasTutorialClass(TutorialClass tutorialClass) { return false; } + /** * List all the tutorial classes under this module. * diff --git a/src/main/java/seedu/address/model/module/TutorialClass.java b/src/main/java/seedu/address/model/module/TutorialClass.java index fe0f899c3c4..2598f4386a7 100644 --- a/src/main/java/seedu/address/model/module/TutorialClass.java +++ b/src/main/java/seedu/address/model/module/TutorialClass.java @@ -24,6 +24,15 @@ public class TutorialClass { public final String tutorialName; private final ArrayList students; + /** + * Constructs a {@code TutorialClass} with default values. + * Initializes the {@code value} field to an empty string and creates an empty list for {@code students}. + */ + public TutorialClass() { + this.tutorialName = ""; + this.students = new ArrayList<>(); + } + /** * A constructor for TutorialClass. Creates an empty tutorial class with no students. @@ -49,7 +58,6 @@ public TutorialClass(String tutorialClass, ArrayList students) { this.tutorialName = tutorialClass; this.students = students; } - /** * Returns true if a given string is a valid tutorial class code. */ diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java index 5efd834091d..3b66418affb 100644 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java @@ -11,6 +11,7 @@ import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.module.ModuleCode; import seedu.address.model.person.Person; /** @@ -20,15 +21,23 @@ class JsonSerializableAddressBook { public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; + public static final String MESSAGE_DUPLICATE_MODULE = "Modules list contains duplicate module(s)."; private final List persons = new ArrayList<>(); + private final List modules = new ArrayList<>(); /** * Constructs a {@code JsonSerializableAddressBook} with the given persons. */ @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); + public JsonSerializableAddressBook(@JsonProperty("persons") List persons, + @JsonProperty("modules") List modules) { + if (persons != null) { + this.persons.addAll(persons); + } + if (modules != null) { + this.modules.addAll(modules); + } } /** @@ -38,6 +47,7 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List readAddressBook() throws DataLoadingExcepti @Override public Optional readAddressBook(Path filePath) throws DataLoadingException { logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); + Optional addressBookOptional = addressBookStorage.readAddressBook(filePath); + + addressBookOptional.ifPresent(addressBook -> { + addressBook.getModuleList().forEach(moduleCode -> { + if (!addressBook.hasModule(moduleCode)) { + addressBook.addModule(moduleCode); + } + }); + }); + + return addressBookOptional; } @Override diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 08c733e2693..bd54605911e 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -2,6 +2,8 @@ import java.util.logging.Logger; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.MenuItem; @@ -14,8 +16,10 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.logic.Logic; import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.ListClassesCommand; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.module.ModuleCode; /** * The Main Window. Provides the basic application layout containing @@ -32,6 +36,7 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private PersonListPanel personListPanel; + private ModuleListPanel moduleListPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -43,6 +48,8 @@ public class MainWindow extends UiPart { @FXML private StackPane personListPanelPlaceholder; + @FXML + private StackPane moduleListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @@ -110,8 +117,11 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + if (initiallyDisplayModuleListPanel()) { + switchToModuleListPanel(); + } else { + switchToPersonListPanel(); + } resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); @@ -123,6 +133,22 @@ void fillInnerParts() { commandBoxPlaceholder.getChildren().add(commandBox.getRoot()); } + private boolean initiallyDisplayModuleListPanel() { + return logic.isInitialModuleListPanelDisplayed(); + } + + private void switchToPersonListPanel() { + personListPanel = new PersonListPanel(logic.getFilteredPersonList()); + personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + } + + private void switchToModuleListPanel() { + ObservableList moduleObservableList = FXCollections + .observableList(logic.getAddressBook().getModuleList()); + moduleListPanel = new ModuleListPanel(moduleObservableList); + moduleListPanelPlaceholder.getChildren().add(moduleListPanel.getRoot()); + } + /** * Sets the default size based on {@code guiSettings}. */ @@ -186,6 +212,14 @@ private CommandResult executeCommand(String commandText) throws CommandException handleExit(); } + clearPanels(); + + if (commandText.equals(ListClassesCommand.COMMAND_WORD)) { + switchToModuleListPanel(); + } else { + switchToPersonListPanel(); + } + return commandResult; } catch (CommandException | ParseException e) { logger.info("An error occurred while executing command: " + commandText); @@ -193,4 +227,9 @@ private CommandResult executeCommand(String commandText) throws CommandException throw e; } } + + private void clearPanels() { + personListPanelPlaceholder.getChildren().clear(); + moduleListPanelPlaceholder.getChildren().clear(); + } } diff --git a/src/main/java/seedu/address/ui/ModuleCard.java b/src/main/java/seedu/address/ui/ModuleCard.java new file mode 100644 index 00000000000..521b5938f54 --- /dev/null +++ b/src/main/java/seedu/address/ui/ModuleCard.java @@ -0,0 +1,32 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.module.ModuleCode; + +/** + * An UI component that displays information of a {@code Module}. + */ +public class ModuleCard extends UiPart { + + private static final String FXML = "ModuleCard.fxml"; + + public final ModuleCode moduleCode; + @FXML + protected Label moduleCodeLabel; + @FXML + protected Label tutorialClassLabel; + @FXML + private HBox cardPane; + /** + * Creates a {@code ModuleCard} with the given {@code Module}. + */ + public ModuleCard(ModuleCode moduleCode) { + super(FXML); + this.moduleCode = moduleCode; + moduleCodeLabel.setText(moduleCode.getModule().toString()); + tutorialClassLabel.setText(moduleCode.getTutorialClasses().toString()); + } +} diff --git a/src/main/java/seedu/address/ui/ModuleListPanel.java b/src/main/java/seedu/address/ui/ModuleListPanel.java new file mode 100644 index 00000000000..db01e7be98d --- /dev/null +++ b/src/main/java/seedu/address/ui/ModuleListPanel.java @@ -0,0 +1,44 @@ +package seedu.address.ui; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import seedu.address.model.module.ModuleCode; + +/** + * Panel containing the list of module codes. + */ +public class ModuleListPanel extends UiPart { + private static final String FXML = "ModuleListPanel.fxml"; + + @FXML + protected ListView moduleListView; + + /** + * Creates a {@code ModuleListPanel} with the given {@code ObservableList}. + */ + public ModuleListPanel(ObservableList moduleCodeList) { + super(FXML); + moduleListView.setItems(moduleCodeList); + moduleListView.setCellFactory(listView -> new ModuleListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code ModuleCode} using a {@code ModuleCard}. + */ + class ModuleListViewCell extends ListCell { + @Override + protected void updateItem(ModuleCode moduleCode, boolean empty) { + super.updateItem(moduleCode, empty); + + if (empty || moduleCode == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ModuleCard(moduleCode).getRoot()); + } + } + } +} diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 76080047eff..a8e267da287 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -26,7 +26,7 @@ - + @@ -53,16 +53,17 @@ - - + + - - + + + diff --git a/src/main/resources/view/ModuleCard.fxml b/src/main/resources/view/ModuleCard.fxml index f80e658d03d..c6df151db9a 100644 --- a/src/main/resources/view/ModuleCard.fxml +++ b/src/main/resources/view/ModuleCard.fxml @@ -1,23 +1,38 @@ + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalModuleAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalModuleAddressBook.json new file mode 100644 index 00000000000..421452adbb7 --- /dev/null +++ b/src/test/data/JsonSerializableAddressBookTest/typicalModuleAddressBook.json @@ -0,0 +1,17 @@ +{ + "_comment": "AddressBook save file which contains the same Module values as in TypicalModules#getTypicalAddressBook()", + "modules": [ + { + "moduleCode": "CS2103T", + "tutorialClass": "T01" + }, + { + "moduleCode": "CS2101", + "tutorialClass": "T02" + }, + { + "moduleCode": "MA1521", + "tutorialClass": "T04" + } + ] +} diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java index 8e7e02410d9..01a04e0bc67 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/address/logic/LogicManagerTest.java @@ -1,6 +1,7 @@ package seedu.address.logic; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; @@ -17,6 +18,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import seedu.address.commons.core.GuiSettings; import seedu.address.logic.commands.AddCommand; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.ListCommand; @@ -170,4 +172,35 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) expectedModel.addPerson(expectedPerson); assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); } + @Test + public void getAddressBook_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> logic.getAddressBook().getPersonList().remove(0)); + } + + @Test + public void getAddressBookFilePath() { + assertEquals(model.getAddressBookFilePath(), logic.getAddressBookFilePath()); + } + + @Test + public void getGuiSettings() { + assertEquals(model.getGuiSettings(), logic.getGuiSettings()); + } + + @Test + public void setGuiSettings() { + GuiSettings newGuiSettings = new GuiSettings(1000, 600, 0, 0); + logic.setGuiSettings(newGuiSettings); + assertEquals(newGuiSettings, logic.getGuiSettings()); + } + + @Test + public void isInitialModuleListPanelDisplayed_emptyAddressBook_returnsFalse() { + assertFalse(logic.isInitialModuleListPanelDisplayed()); + } + @Test + public void isInitialModuleListPanelDisplayed_nonEmptyAddressBook_returnsTrue() { + model.addPerson(new PersonBuilder().build()); + assertFalse(logic.isInitialModuleListPanelDisplayed()); + } } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java index 84685764e76..c72568729da 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java @@ -149,11 +149,6 @@ public void addModule(ModuleCode module) { throw new AssertionError("This method should not be called."); } - @Override - public boolean hasModule(ModuleCode module) { - throw new AssertionError("This method should not be called."); - } - @Override public ModuleCode findModuleFromList(ModuleCode module) { throw new AssertionError("This method should not be called."); @@ -168,12 +163,21 @@ public ObservableList getFilteredPersonList() { throw new AssertionError("This method should not be called."); } + @Override + public ObservableList getFilteredModuleList() { + throw new AssertionError("This method should not be called."); + } + @Override public void updateFilteredPersonList(Predicate predicate) { throw new AssertionError("This method should not be called."); } @Override + public void updateFilteredModuleList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + public Person searchPersonByPredicate(Predicate predicate) { throw new AssertionError("This method should not be called."); } diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java index 819a73a5400..b2817edf5ec 100644 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ b/src/test/java/seedu/address/model/AddressBookTest.java @@ -105,8 +105,17 @@ public ObservableList getPersonList() { } @Override - public List getModuleList() { + public ObservableList getModuleList() { return null; } + + @Override + public boolean hasModule(ModuleCode moduleCode) { + return false; + } + + @Override + public void addModule(ModuleCode moduleCode) { + } } } diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java index 2cf1418d116..3600d57323f 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/seedu/address/model/ModelManagerTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_MODULES; import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; import static seedu.address.testutil.Assert.assertThrows; import static seedu.address.testutil.TypicalPersons.ALICE; @@ -92,6 +93,10 @@ public void hasPerson_personInAddressBook_returnsTrue() { public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0)); } + @Test + public void getFilteredModuleList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredModuleList().remove(0)); + } @Test public void equals() { @@ -123,6 +128,7 @@ public void equals() { // resets modelManager to initial state for upcoming tests modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + modelManager.updateFilteredModuleList(PREDICATE_SHOW_ALL_MODULES); // different userPrefs -> returns false UserPrefs differentUserPrefs = new UserPrefs(); diff --git a/src/test/java/seedu/address/model/module/TutorialClassTest.java b/src/test/java/seedu/address/model/module/TutorialClassTest.java index 591db482a65..3ffa1438963 100644 --- a/src/test/java/seedu/address/model/module/TutorialClassTest.java +++ b/src/test/java/seedu/address/model/module/TutorialClassTest.java @@ -3,14 +3,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static seedu.address.model.module.TutorialClass.isValidTutorialClass; import static seedu.address.testutil.TypicalPersons.ALICE; +import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; import org.junit.jupiter.api.Test; +import seedu.address.model.person.Email; +import seedu.address.model.person.Name; import seedu.address.model.person.Person; +import seedu.address.model.person.StudentId; +import seedu.address.model.tag.Tag; import seedu.address.testutil.ModuleBuilder; import seedu.address.testutil.PersonBuilder; @@ -93,4 +101,45 @@ void testToString_success() { TutorialClass tutorial = new TutorialClass(VALID_TUTORIAL); assertEquals(tutorial.toString(), VALID_TUTORIAL); } + + @Test + void testEmptyConstructor() { + TutorialClass tutorialClass = new TutorialClass(); + assertEquals("", tutorialClass.getTutorialClass().toString()); + try { + Field field = TutorialClass.class.getDeclaredField("students"); + field.setAccessible(true); // Allow access to private field + Object value = field.get(tutorialClass); // Get the value of the field + assertTrue(((ArrayList) value).isEmpty()); // Check if it's empty + } catch (NoSuchFieldException | IllegalAccessException e) { + fail("Exception thrown while accessing private field: " + e.getMessage()); + } + } + + @Test + void testConstructorWithStudents() { + ArrayList students = new ArrayList<>(); + + // Creating Person objects + Name name1 = new Name("John"); + Email email1 = new Email("john@example.com"); + StudentId stuId1 = new StudentId("A0213333J"); + Set tags1 = new HashSet<>(); + Person student1 = new Person(name1, email1, stuId1, tags1); + + Name name2 = new Name("Alice"); + Email email2 = new Email("alice@example.com"); + StudentId stuId2 = new StudentId("A0145678A"); + Set tags2 = new HashSet<>(); + Person student2 = new Person(name2, email2, stuId2, tags2); + + students.add(student1); + students.add(student2); + + TutorialClass tutorialClass = new TutorialClass(VALID_TUTORIAL, students); + assertEquals(VALID_TUTORIAL, tutorialClass.getTutorialClass().toString()); + assertEquals(2, tutorialClass.getStudents().size()); // Accessing students via getter + assertTrue(tutorialClass.getStudents().contains(student1)); + assertTrue(tutorialClass.getStudents().contains(student2)); + } } diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java index 188c9058d20..f93c54f3416 100644 --- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java +++ b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java @@ -1,16 +1,20 @@ package seedu.address.storage; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static seedu.address.testutil.Assert.assertThrows; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.commons.util.JsonUtil; import seedu.address.model.AddressBook; +import seedu.address.model.module.ModuleCode; import seedu.address.testutil.TypicalPersons; public class JsonSerializableAddressBookTest { @@ -19,6 +23,7 @@ public class JsonSerializableAddressBookTest { private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); + private static final Path TYPICAL_MODULES_FILE = TEST_DATA_FOLDER.resolve("typicalModulesAddressBook.json"); @Test public void toModelType_typicalPersonsFile_success() throws Exception { @@ -43,5 +48,18 @@ public void toModelType_duplicatePersons_throwsIllegalValueException() throws Ex assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, dataFromFile::toModelType); } + @Test + public void toModelType_nullModuleList_throwsIllegalValueException() { + JsonSerializableAddressBook data = new JsonSerializableAddressBook(new ArrayList<>(), null); + assertDoesNotThrow(data::toModelType); + } + @Test + public void toModelType_duplicateModules_throwsIllegalValueException() { + List modules = new ArrayList<>(); + modules.add(new JsonAdaptedModule(new ModuleCode("CS1010"))); + modules.add(new JsonAdaptedModule(new ModuleCode("CS1010"))); + JsonSerializableAddressBook data = new JsonSerializableAddressBook(new ArrayList<>(), modules); + assertThrows(IllegalValueException.class, data::toModelType); + } } diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/address/storage/StorageManagerTest.java index 99a16548970..8b1025ee576 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/address/storage/StorageManagerTest.java @@ -2,6 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; import java.nio.file.Path; @@ -14,6 +15,7 @@ import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.UserPrefs; +import seedu.address.model.module.ModuleCode; public class StorageManagerTest { @@ -64,5 +66,42 @@ public void addressBookReadSave() throws Exception { public void getAddressBookFilePath() { assertNotNull(storageManager.getAddressBookFilePath()); } + @Test + public void getUserPrefsFilePath() { + assertNotNull(storageManager.getUserPrefsFilePath()); + } + + @Test + public void readAddressBook_withModulesWithoutModuleList_addsModulesToList() throws Exception { + AddressBook addressBook = getTypicalAddressBook(); + JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + StorageManager storageManager = new StorageManager(addressBookStorage, + new JsonUserPrefsStorage(getTempFilePath("prefs"))); + storageManager.saveAddressBook(addressBook); + + // Modify the storage file to remove module list + addressBookStorage.saveAddressBook(new AddressBook(), addressBookStorage.getAddressBookFilePath()); + + ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); + assertTrue(retrieved.getModuleList().isEmpty()); + } + + @Test + public void readAddressBook_addsModulesToListIfNotPresent() throws Exception { + AddressBook addressBook = new AddressBook(); + // Add some modules to the address book + addressBook.addModule(new ModuleCode("CS1010")); + addressBook.addModule(new ModuleCode("MA1505")); + + JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + StorageManager storageManager = new StorageManager(addressBookStorage, + new JsonUserPrefsStorage(getTempFilePath("prefs"))); + storageManager.saveAddressBook(addressBook); + + ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); + // Check if modules are added correctly + assertTrue(retrieved.hasModule(new ModuleCode("CS1010"))); + assertTrue(retrieved.hasModule(new ModuleCode("MA1505"))); + } } diff --git a/src/test/java/seedu/address/ui/JavaFxInitializer.java b/src/test/java/seedu/address/ui/JavaFxInitializer.java new file mode 100644 index 00000000000..8f606099c96 --- /dev/null +++ b/src/test/java/seedu/address/ui/JavaFxInitializer.java @@ -0,0 +1,15 @@ +package seedu.address.ui; + +import org.junit.jupiter.api.BeforeAll; + +import javafx.embed.swing.JFXPanel; +/** + * The JavaFxInitializer class provides a method to initialize the JavaFX environment + * before running any JavaFX-related tests. + */ +public class JavaFxInitializer { + @BeforeAll + public static void initializeJavaFX() { + new JFXPanel(); // Initializes JavaFX environment + } +}