Skip to content

Commit

Permalink
Merge branch 'master' into branch-Documentation/DGSkeleton
Browse files Browse the repository at this point in the history
* master:
  Add Ui unit tests for CommandBox
  Fix magic values issue
  Update version number
  Fix style
  Add comment to ESCAPE key behaviour
  Update cursor behaviour for command history
  Improve code style and rename remaining addressbook references
  Add keypress commandhistory functionality
  • Loading branch information
sp4ce-cowboy committed Oct 28, 2023
2 parents 92e73be + 5def81f commit 059278a
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 20 deletions.
8 changes: 4 additions & 4 deletions src/main/java/unicash/MainApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
*/
public class MainApp extends Application {

public static final Version VERSION = new Version(1, 2, 0, true);
public static final Version VERSION = new Version(1, 3, 0, true);

private static final Logger logger = LogsCenter.getLogger(MainApp.class);

Expand Down Expand Up @@ -82,9 +82,9 @@ public void init() throws Exception {
}

/**
* Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}. <br>
* The data from the sample address book will be used instead if {@code storage}'s address book is not found,
* or an empty address book will be used instead if errors occur when reading {@code storage}'s address book.
* Returns a {@code ModelManager} with the data from {@code storage}'s UniCash and {@code userPrefs}. <br>
* The data from the sample UniCash will be used instead if {@code storage}'s UniCash is not found,
* or an empty UniCash will be used instead if errors occur when reading {@code storage}'s UniCash.
*/
private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
logger.info("Using data file : " + storage.getUniCashFilePath());
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/unicash/commons/core/LogsCenter.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
public class LogsCenter {
private static final int MAX_FILE_COUNT = 5;
private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB
private static final String LOG_FILE = "addressbook.log";
private static final String LOG_FILE = "unicash.log";
private static final Logger logger; // logger for this class
private static Logger baseLogger; // to be used as the parent of all other loggers created by this class.
private static Level currentLogLevel = Level.INFO;
Expand Down Expand Up @@ -79,7 +79,7 @@ private static void removeHandlers(Logger logger) {
* Sets it as the {@code baseLogger}, to be used as the parent logger of all other loggers.
*/
private static void setBaseLogger() {
baseLogger = Logger.getLogger("ab3");
baseLogger = Logger.getLogger("unicash");
baseLogger.setUseParentHandlers(false);
removeHandlers(baseLogger);

Expand Down
124 changes: 114 additions & 10 deletions src/main/java/unicash/ui/CommandBox.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package unicash.ui;

import java.util.ArrayList;
import java.util.List;

import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
Expand All @@ -9,19 +12,33 @@
import unicash.logic.commands.exceptions.CommandException;
import unicash.logic.parser.exceptions.ParseException;


/**
* The UI component that is responsible for receiving user command inputs.
*
* </p> Each user input, regardless of the correctness of input and as long as
* the input is not blank, will be stored in a User Input History {@code ArrayList}.
* The user can traverse through this history with the standard Keyboard Input
* {@code UP} and {@code DOWN} arrow keys. The User Input History only persists
* during the application's runtime, and will be reset once the application is closed.
*/
public class CommandBox extends UiPart<Region> {

public static final String ERROR_STYLE_CLASS = "error";
private static final String FXML = "CommandBox.fxml";
private static final int DEFAULT_INDEX = -1;

private final CommandExecutor commandExecutor;

@FXML
private TextField commandTextField;

// To keep a history of previous userInputs
private List<String> userInputHistory = new ArrayList<>();

// To keep track of which userInput the user is currently on
private int currentUserInputIndex = DEFAULT_INDEX;

/**
* Creates a {@code CommandBox} with the given {@code CommandExecutor}.
*/
Expand All @@ -30,6 +47,51 @@ public CommandBox(CommandExecutor commandExecutor) {
this.commandExecutor = commandExecutor;
// calls #setStyleToDefault() whenever there is a change to the text of the command box.
commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault());

/* Iterates through history of userInputs held in the userInputHistory List
* the arrow "UP" key and arrow "DOWN" key are constants defined by the KeyCode
* class, and user keyboard input is observed by the KeyEvent class. The user can
* also input the "ESCAPE" key to blank the field, instead of having to manually
* backspace through the command text field */
commandTextField.setOnKeyPressed(event -> {
switch (event.getCode()) {
case UP:
showPreviousUserInput();
commandTextField.end(); // Moves the text cursor to the end of the TextField
break;
case DOWN:
showNextUserInput();
commandTextField.end(); // Moves the text cursor to the end of the TextField
break;
case ESCAPE:
clearCommandTextField(); // Clears the command text field while MainWindow is in focus
break;
default:
break;
}
});
}

/**
* Represents a function that can execute commands.
*/
@FunctionalInterface
public interface CommandExecutor {
/**
* Executes the command and returns the result.
*
* @see Logic#execute(String)
*/
CommandResult execute(String commandText) throws CommandException, ParseException;
}

/**
* Returns the command text field as a TextField object
*
* @return the command text field
*/
public TextField getCommandTextField() {
return this.commandTextField;
}

/**
Expand All @@ -43,8 +105,9 @@ private void handleCommandEntered() {
}

try {
addUserInputToHistory(commandText);
commandExecutor.execute(commandText);
commandTextField.setText("");
clearCommandTextField();
} catch (CommandException | ParseException e) {
setStyleToIndicateCommandFailure();
}
Expand All @@ -71,16 +134,57 @@ private void setStyleToIndicateCommandFailure() {
}

/**
* Represents a function that can execute commands.
* Adds the input string to command history list and resets command index.
*/
@FunctionalInterface
public interface CommandExecutor {
/**
* Executes the command and returns the result.
*
* @see Logic#execute(String)
*/
CommandResult execute(String commandText) throws CommandException, ParseException;
private void addUserInputToHistory(String input) {
userInputHistory.add(input);
currentUserInputIndex = DEFAULT_INDEX; // reset index as new command is added
}

/**
* Clears the Command Text Field.
*/
public void clearCommandTextField() {
commandTextField.setText("");
}

/**
* Iterates through userInput history backwards and sets the commandTextField text
* stopping at the earliest entered user input.
*/
private void showPreviousUserInput() {
if (userInputHistory.isEmpty()) {
return;
}

if (currentUserInputIndex == DEFAULT_INDEX) {
currentUserInputIndex = userInputHistory.size() - 1;

} else if (currentUserInputIndex > 0) {
currentUserInputIndex--;
}

commandTextField.setText(userInputHistory.get(currentUserInputIndex));
}

/**
* Iterates through userInput history backwards and sets the commandTextField text
* stopping at the latest state of the CommandTextField, which is cleared by default.
*/
private void showNextUserInput() {
if (userInputHistory.isEmpty() || currentUserInputIndex == DEFAULT_INDEX) {
return;
}

if (currentUserInputIndex < userInputHistory.size() - 1) {
// If not at the end of the list, move one step forward i.e. down
currentUserInputIndex++;
commandTextField.setText(userInputHistory.get(currentUserInputIndex));

} else {
// If at the end of the list, reset index to -1 and clear text field
currentUserInputIndex = DEFAULT_INDEX;
clearCommandTextField();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public Path getUniCashFilePath() {
}

@Override
public void setUniCashFilePath(Path addressBookFilePath) {
public void setUniCashFilePath(Path uniCashFilePath) {
throw new AssertionError("This method should not be called.");
}

Expand Down
3 changes: 2 additions & 1 deletion src/test/java/unicash/logic/commands/CommandTestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ public static void assertCommandSuccess(Command command, Model actualModel, Stri
* Executes the given {@code command}, confirms that <br>
* - a {@code CommandException} is thrown <br>
* - the CommandException message matches {@code expectedMessage} <br>
* - the address book, filtered person list and selected person in {@code actualModel} remain unchanged
* - the UniCash model, filtered transactions list and selected transaction in
* {@code actualModel} remain unchanged
*/
public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) {
// we are unable to defensively copy the model for comparison later, so we can
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/unicash/storage/StorageManagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public void prefsReadSave() throws Exception {
public void uniCashReadSave() throws Exception {
/*
* Note: This is an integration test that verifies the StorageManager is properly wired to the
* {@link JsonAddressBookStorage} class.
* More extensive testing of UserPref saving/reading is done in {@link JsonAddressBookStorageTest} class.
* {@link JsonUniCashStorage} class.
* More extensive testing of UserPref saving/reading is done in {@link JsonUniCashStorageTest} class.
*/
var original = TypicalTransactions.getTypicalUniCash();
storageManager.saveUniCash(original);
Expand Down
88 changes: 88 additions & 0 deletions src/test/java/unicash/ui/CommandBoxUiTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package unicash.ui;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.testfx.api.FxRobot;
import org.testfx.framework.junit5.ApplicationExtension;
import org.testfx.framework.junit5.Start;

import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;


@ExtendWith(ApplicationExtension.class)
public class CommandBoxUiTest {

private CommandBox commandBox;

@Start
public void start(Stage stage) {
CommandBox.CommandExecutor dummyExecutor = (commandText) -> {
return null; // A dummy implementation
};
commandBox = new CommandBox(dummyExecutor);
stage.setScene(new Scene(commandBox.getRoot()));
stage.show();
}

@Test
public void upArrowKey_showsPreviousUserInput(FxRobot robot) {
robot.clickOn(commandBox.getCommandTextField())
.write("testCommand")
.type(KeyCode.ENTER)
.write("anotherTestCommand")
.type(KeyCode.ENTER)
.type(KeyCode.UP)
.type(KeyCode.UP);

assertEquals("testCommand", commandBox.getCommandTextField().getText());
}

@Test
public void downArrowKey_showsNextUserInput(FxRobot robot) {
robot.clickOn(commandBox.getCommandTextField())
.write("testCommand")
.type(KeyCode.ENTER)
.write("anotherTestCommand")
.type(KeyCode.ENTER)
.type(KeyCode.UP)
.type(KeyCode.UP)
.type(KeyCode.DOWN);

assertEquals("anotherTestCommand", commandBox.getCommandTextField().getText());
}

@Test
public void escapeKey_clearsUserInput(FxRobot robot) {
robot.clickOn(commandBox.getCommandTextField())
.write("testCommand")
.type(KeyCode.ESCAPE)
.write("anotherTestCommand")
.type(KeyCode.ESCAPE)
.type(KeyCode.UP)
.type(KeyCode.UP)
.type(KeyCode.ESCAPE);

assertEquals("", commandBox.getCommandTextField().getText());
}

@Test
public void downKeyMultipleTimes_showsClearedUserInput(FxRobot robot) {
robot.clickOn(commandBox.getCommandTextField())
.write("testCommand")
.type(KeyCode.ENTER)
.write("anotherTestCommand")
.type(KeyCode.ENTER)
.type(KeyCode.UP)
.type(KeyCode.UP)
.type(KeyCode.DOWN)
.type(KeyCode.DOWN)
.type(KeyCode.DOWN);

assertEquals("", commandBox.getCommandTextField().getText());
}

}

0 comments on commit 059278a

Please sign in to comment.