diff --git a/docs/UserGuide.adoc b/docs/UserGuide.adoc index 372caae1cc82..a98f5a9fd01b 100644 --- a/docs/UserGuide.adoc +++ b/docs/UserGuide.adoc @@ -96,23 +96,27 @@ Edits the name of the 2nd person to be `Betsy Crower` and clears all existing ta === Locating persons by name: `find` -Finds persons whose names contain any of the given keywords. + -Format: `find KEYWORD [MORE_KEYWORDS]` +Finds persons whose fields contain any of the given keywords. + +Format: +Example1: `find KEYWORD [MORE_KEYWORDS]` +Example2: [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG] **** * The search is case insensitive. e.g `hans` will match `Hans` * The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` -* Only the name is searched. * Only full words will be matched e.g. `Han` will not match `Hans` +* If there is no prefix, all fields will be searched * Persons matching at least one keyword will be returned (i.e. `OR` search). e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` **** Examples: -* `find John` + -Returns `john` and `John Doe` -* `find Betsy Tim John` + -Returns any person having names `Betsy`, `Tim`, or `John` +* `find John PGP` + +Returns any person having fields with keywords `John` or `PGP` +* `find n/Betsy t/friends` + +Returns the person having name `Betsy` AND tag `friends` +* `find t/friends teammate` + +Returns any person having tag `friends` OR tag `teammate` === Deleting a person : `delete` @@ -239,7 +243,7 @@ _{explain how the user can enable/disable data encryption}_ Redirect user to external email service on browser to email a specific email address -Format: `email NAME` +Format: `email INDEX` === Sorting the name in contact list alphabetically : `sort` diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index beb178e3a3f5..66cc64606aaa 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -2,30 +2,42 @@ import static java.util.Objects.requireNonNull; +import java.util.function.Predicate; + import seedu.address.commons.core.Messages; import seedu.address.logic.CommandHistory; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.person.Person; /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all persons in address book + * whose field(name, address, email, phone) contains any of the argument keywords. * Keyword matching is case insensitive. */ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all persons whose field contain any of " + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; + + "Parameters: [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]...\n" + + "Example1: " + COMMAND_WORD + " yinya alex " + + "\\if there's no prefix, all fields will be searched\n" + + "Example2: " + COMMAND_WORD + " p/123456 t/teammate\n" + + "Example3: " + COMMAND_WORD + " t/friends teammate\n"; + - private final NameContainsKeywordsPredicate predicate; + //private final NameContainsKeywordsPredicate predicate;//todo delete? + public static final String MESSAGE_NO_PARAMETER = "Must provide at least one parameters to find."; + private Predicate predicate; - public FindCommand(NameContainsKeywordsPredicate predicate) { + + public FindCommand(Predicate predicate) { this.predicate = predicate; } + @Override public CommandResult execute(Model model, CommandHistory history) { requireNonNull(model); @@ -40,4 +52,9 @@ public boolean equals(Object other) { || (other instanceof FindCommand // instanceof handles nulls && predicate.equals(((FindCommand) other).predicate)); // state check } + + public Predicate getPredicate() { + return predicate; + } } + diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf1190..1b4ff730c73f 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -11,5 +11,5 @@ public class CliSyntax { public static final Prefix PREFIX_EMAIL = new Prefix("e/"); public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); - + public static final Prefix PREFIX_AVATOR = new Prefix("av/"); } diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java index b186a967cb94..814270c09a95 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java @@ -1,21 +1,48 @@ package seedu.address.logic.parser; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import java.util.ArrayList; import java.util.Arrays; +import java.util.function.Predicate; +import java.util.stream.Stream; import seedu.address.logic.commands.FindCommand; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.person.AddressContainsKeywordPredicate; +import seedu.address.model.person.EmailContainsKeywordPredicate; import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.person.Person; +import seedu.address.model.person.PhoneContainsKeywordPredicate; +import seedu.address.model.person.TagsContainsKeywordPredicate; + /** * Parses input arguments and creates a new FindCommand object */ public class FindCommandParser implements Parser { + /** + * check if there's a prefix in the command + */ + private boolean hasPrefix(String command) { + String[] commands = command.split("\\s+"); + + return (commands[0].contains(PREFIX_NAME.toString()) || commands[0].contains(PREFIX_EMAIL.toString()) + || commands[0].contains(PREFIX_ADDRESS.toString()) + || commands[0].contains(PREFIX_PHONE.toString()) + || commands[0].contains(PREFIX_TAG.toString())); + } + /** * Parses the given {@code String} of arguments in the context of the FindCommand * and returns an FindCommand object for execution. + * * @throws ParseException if the user input does not conform the expected format */ public FindCommand parse(String args) throws ParseException { @@ -25,9 +52,75 @@ public FindCommand parse(String args) throws ParseException { String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } - String[] nameKeywords = trimmedArgs.split("\\s+"); + //String[] nameKeywords = trimmedArgs.split("\\s+"); + //return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize( + args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArrayList> predicates = new ArrayList<>(); + Predicate predicateResult; + + // if there's no prefix, find in all fields + if (!hasPrefix(trimmedArgs)) { + String[] splitedKeywords = trimmedArgs.split("\\s+"); + + predicates.add(new NameContainsKeywordsPredicate(Arrays.asList(splitedKeywords))); + predicates.add(new PhoneContainsKeywordPredicate(Arrays.asList(splitedKeywords))); + predicates.add(new EmailContainsKeywordPredicate(Arrays.asList(splitedKeywords))); + predicates.add(new AddressContainsKeywordPredicate(Arrays.asList(splitedKeywords))); + predicates.add(new TagsContainsKeywordPredicate(Arrays.asList(splitedKeywords))); + + Predicate[] predicatesList = predicates.toArray(new Predicate[predicates.size()]); + predicateResult = Stream.of(predicatesList).reduce(condition -> false, Predicate::or); + + return new FindCommand(predicateResult); + } + + // create find Command according to the specific prefix + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + String[] nameList = argMultimap.getValue(PREFIX_NAME).get().split("\\s+"); + predicates.add(new NameContainsKeywordsPredicate(Arrays.asList(nameList))); + } + + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + String[] emailList = argMultimap.getValue(PREFIX_EMAIL).get().split("\\s+"); + predicates.add(new EmailContainsKeywordPredicate(Arrays.asList(emailList))); + } + + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + String[] phoneList = argMultimap.getValue(PREFIX_PHONE).get().split("\\s+"); + predicates.add(new PhoneContainsKeywordPredicate(Arrays.asList(phoneList))); + } + + if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { + String[] addressList = argMultimap.getValue(PREFIX_ADDRESS).get().split("\\s+"); + predicates.add(new AddressContainsKeywordPredicate(Arrays.asList(addressList))); + } + + if (argMultimap.getValue(PREFIX_TAG).isPresent()) { + String[] tagList = argMultimap.getValue(PREFIX_TAG).get().split("\\s+"); + predicates.add(new TagsContainsKeywordPredicate(Arrays.asList(tagList))); + } + + + Predicate[] predicatesList = predicates.toArray(new Predicate[predicates.size()]); + predicateResult = Stream.of(predicatesList).reduce(condition -> true, Predicate::and); + + return new FindCommand(predicateResult); + - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); } + } + + + + + + + + + + + diff --git a/src/main/java/seedu/address/model/person/AddressContainsKeywordPredicate.java b/src/main/java/seedu/address/model/person/AddressContainsKeywordPredicate.java new file mode 100644 index 000000000000..d4f8cf70cb24 --- /dev/null +++ b/src/main/java/seedu/address/model/person/AddressContainsKeywordPredicate.java @@ -0,0 +1,31 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Person}'s {@code Address} matches any of the keywords given. + */ +public class AddressContainsKeywordPredicate implements Predicate { + private final List keywords; + + public AddressContainsKeywordPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getAddress().value, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this || (other instanceof AddressContainsKeywordPredicate + && this.keywords.equals(((AddressContainsKeywordPredicate) other).keywords)); + } + +} + diff --git a/src/main/java/seedu/address/model/person/EmailContainsKeywordPredicate.java b/src/main/java/seedu/address/model/person/EmailContainsKeywordPredicate.java new file mode 100644 index 000000000000..32bd17752b62 --- /dev/null +++ b/src/main/java/seedu/address/model/person/EmailContainsKeywordPredicate.java @@ -0,0 +1,31 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Person}'s {@code Email} matches any of the keywords given. + */ +public class EmailContainsKeywordPredicate implements Predicate { + private final List keywords; + + public EmailContainsKeywordPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getEmail().value, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this || (other instanceof EmailContainsKeywordPredicate + && this.keywords.equals(((EmailContainsKeywordPredicate) other).keywords)); + } + +} + diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index 557a7a60cd51..40ec59aa36af 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -60,6 +60,14 @@ public Set getTags() { return Collections.unmodifiableSet(tags); } + public String getTagsAsStringNoBracket() { + StringBuffer sb = new StringBuffer(""); + for (Tag tag: tags) { + sb.append(tag.toStringNoBracket() + " "); + } + return sb.toString(); + } + /** * Returns true if both persons of the same name have at least one other identity field that is the same. * This defines a weaker notion of equality between two persons. diff --git a/src/main/java/seedu/address/model/person/PhoneContainsKeywordPredicate.java b/src/main/java/seedu/address/model/person/PhoneContainsKeywordPredicate.java new file mode 100644 index 000000000000..b6ab8f01ba6c --- /dev/null +++ b/src/main/java/seedu/address/model/person/PhoneContainsKeywordPredicate.java @@ -0,0 +1,31 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Person}'s {@code Phone} matches any of the keywords given. + */ +public class PhoneContainsKeywordPredicate implements Predicate { + private final List keywords; + + public PhoneContainsKeywordPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getPhone().value, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this || (other instanceof PhoneContainsKeywordPredicate + && this.keywords.equals(((PhoneContainsKeywordPredicate) other).keywords)); + } + +} + diff --git a/src/main/java/seedu/address/model/person/TagsContainsKeywordPredicate.java b/src/main/java/seedu/address/model/person/TagsContainsKeywordPredicate.java new file mode 100644 index 000000000000..80fda31e694d --- /dev/null +++ b/src/main/java/seedu/address/model/person/TagsContainsKeywordPredicate.java @@ -0,0 +1,30 @@ +package seedu.address.model.person; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; + +/** + * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + */ +public class TagsContainsKeywordPredicate implements Predicate { + private final List keywords; + + public TagsContainsKeywordPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getTagsAsStringNoBracket(), keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this || (other instanceof TagsContainsKeywordPredicate // instanceof handles nulls + && keywords.equals(((TagsContainsKeywordPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java index b0ea7e7dad7f..3a9f4b029832 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/address/model/tag/Tag.java @@ -32,6 +32,7 @@ public static boolean isValidTagName(String test) { return test.matches(VALIDATION_REGEX); } + @Override public boolean equals(Object other) { return other == this // short circuit if same object @@ -51,4 +52,8 @@ public String toString() { return '[' + tagName + ']'; } + public String toStringNoBracket() { + return tagName; + } + } diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java index a8c8cbf21f4e..19ea022885bf 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java @@ -28,7 +28,6 @@ import seedu.address.logic.commands.SelectCommand; import seedu.address.logic.commands.UndoCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; import seedu.address.model.person.Person; import seedu.address.testutil.EditPersonDescriptorBuilder; import seedu.address.testutil.PersonBuilder; @@ -78,9 +77,16 @@ public void parseCommand_exit() throws Exception { @Test public void parseCommand_find() throws Exception { List keywords = Arrays.asList("foo", "bar", "baz"); - FindCommand command = (FindCommand) parser.parseCommand( - FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); - assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); + // FindCommand command = (FindCommand) parser.parseCommand( + // FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); + // assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); + + + assertTrue(parser.parseCommand( + FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))) + instanceof FindCommand); + assertTrue(parser.parseCommand(FindCommand.COMMAND_WORD + " n/baz") instanceof FindCommand); + //todo } @Test diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java index e65143d3b7b0..1682f8b9f0ea 100644 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java @@ -2,14 +2,10 @@ import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; - -import java.util.Arrays; import org.junit.Test; import seedu.address.logic.commands.FindCommand; -import seedu.address.model.person.NameContainsKeywordsPredicate; public class FindCommandParserTest { @@ -20,15 +16,25 @@ public void parse_emptyArg_throwsParseException() { assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); } - @Test + /*@Test public void parse_validArgs_returnsFindCommand() { - // no leading and trailing whitespaces + String Command = "n/Alice Bob"; + + String[] splitedKeywords = Command.trim().split("\\s+"); + ArrayList> predicates = new ArrayList<>(); + + predicates.add(new NameContainsKeywordsPredicate(Arrays.asList(splitedKeywords))); + + + Predicate[] predicatesList = predicates.toArray(new Predicate[predicates.size()]); + Predicate PredicateResult = Stream.of(predicatesList).reduce(condition -> false, Predicate::or); + no leading and trailing whitespaces FindCommand expectedFindCommand = - new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"))); - assertParseSuccess(parser, "Alice Bob", expectedFindCommand); + new FindCommand(PredicateResult); + assertParseSuccess(parser, "n/Alice Bob", expectedFindCommand); - // multiple whitespaces between keywords + multiple whitespaces between keywords assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand); } - +*/ } diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java index 76841215e3a0..b86fcd532914 100644 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java @@ -67,9 +67,10 @@ public void test_nameDoesNotContainKeywords_returnsFalse() { predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - // Keywords match phone, email and address, but does not match name - predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street")); + // Keywords match phone, email and address and tag, but does not match name + predicate = new NameContainsKeywordsPredicate( + Arrays.asList("12345", "alice@email.com", "Main", "Street", "family")); assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345") - .withEmail("alice@email.com").withAddress("Main Street").build())); + .withEmail("alice@email.com").withAddress("Main Street").withTags("family").build())); } } diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java index fec76fb71293..0de4d07f3ed7 100644 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ b/src/test/java/seedu/address/testutil/TypicalPersons.java @@ -57,6 +57,16 @@ public class TypicalPersons { public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER + public static final Person YINYA = new PersonBuilder().withName("Chen Yinya") + .withAddress("Utown") + .withEmail("yinya@example.com").withPhone("12345") + .withTags("teammate").build(); + + public static final Person KAI = new PersonBuilder().withName("Kai Song") + .withAddress("PGP") + .withEmail("kai@example.com").withPhone("67890") + .withTags("owesMoney").build(); + private TypicalPersons() {} // prevents instantiation /** diff --git a/src/test/java/systemtests/FindCommandSystemTest.java b/src/test/java/systemtests/FindCommandSystemTest.java index 0bde83c0444b..49f239667226 100644 --- a/src/test/java/systemtests/FindCommandSystemTest.java +++ b/src/test/java/systemtests/FindCommandSystemTest.java @@ -3,10 +3,13 @@ import static org.junit.Assert.assertFalse; import static seedu.address.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.address.testutil.TypicalPersons.ALICE; import static seedu.address.testutil.TypicalPersons.BENSON; import static seedu.address.testutil.TypicalPersons.CARL; import static seedu.address.testutil.TypicalPersons.DANIEL; +import static seedu.address.testutil.TypicalPersons.KAI; import static seedu.address.testutil.TypicalPersons.KEYWORD_MATCHING_MEIER; +import static seedu.address.testutil.TypicalPersons.YINYA; import java.util.ArrayList; import java.util.List; @@ -94,41 +97,44 @@ public void find() { assertCommandSuccess(command, expectedModel); assertSelectedCardUnchanged(); - /* Case: find person in address book, keyword is substring of name -> 0 persons found */ + /* Case: find person in address book, keyword is substring of name -> 0 persons found*/ command = FindCommand.COMMAND_WORD + " Mei"; ModelHelper.setFilteredList(expectedModel); assertCommandSuccess(command, expectedModel); assertSelectedCardUnchanged(); - /* Case: find person in address book, name is substring of keyword -> 0 persons found */ + /* Case: find person in address book, name is substring of keyword -> 0 persons found*/ command = FindCommand.COMMAND_WORD + " Meiers"; ModelHelper.setFilteredList(expectedModel); assertCommandSuccess(command, expectedModel); assertSelectedCardUnchanged(); - /* Case: find person not in address book -> 0 persons found */ + /* Case: find person not in address book -> 0 persons found*/ command = FindCommand.COMMAND_WORD + " Mark"; assertCommandSuccess(command, expectedModel); assertSelectedCardUnchanged(); - /* Case: find phone number of person in address book -> 0 persons found */ + /* Case: find phone number of person in address book -> 1 persons found */ command = FindCommand.COMMAND_WORD + " " + DANIEL.getPhone().value; + ModelHelper.setFilteredList(expectedModel, DANIEL); assertCommandSuccess(command, expectedModel); assertSelectedCardUnchanged(); - /* Case: find address of person in address book -> 0 persons found */ + /* Case: find address of person in address book -> 1 persons found */ command = FindCommand.COMMAND_WORD + " " + DANIEL.getAddress().value; - assertCommandSuccess(command, expectedModel); + ModelHelper.setFilteredList(expectedModel, DANIEL); assertSelectedCardUnchanged(); - /* Case: find email of person in address book -> 0 persons found */ + /* Case: find email of person in address book -> 1 persons found */ command = FindCommand.COMMAND_WORD + " " + DANIEL.getEmail().value; + ModelHelper.setFilteredList(expectedModel, DANIEL); assertCommandSuccess(command, expectedModel); assertSelectedCardUnchanged(); - /* Case: find tags of person in address book -> 0 persons found */ + /* Case: find tags of person in address book -> 3 persons found */ List tags = new ArrayList<>(DANIEL.getTags()); command = FindCommand.COMMAND_WORD + " " + tags.get(0).tagName; + ModelHelper.setFilteredList(expectedModel, DANIEL, BENSON, ALICE); assertCommandSuccess(command, expectedModel); assertSelectedCardUnchanged(); @@ -152,6 +158,29 @@ public void find() { /* Case: mixed case command word -> rejected */ command = "FiNd Meier"; assertCommandFailure(command, MESSAGE_UNKNOWN_COMMAND); + + /* Case: search keywords in all fields if there's no prefix -> 2 persons found*/ + command = FindCommand.COMMAND_WORD + " yinya PGP"; + ModelHelper.setFilteredList(expectedModel, YINYA, KAI); + assertCommandSuccess(command, expectedModel); + assertSelectedCardUnchanged(); + + /* Case: search multiple keywords in same fields -> 4 persons found*/ + command = FindCommand.COMMAND_WORD + " t/friends teammate"; + ModelHelper.setFilteredList(expectedModel, DANIEL, BENSON, ALICE, YINYA); + assertCommandSuccess(command, expectedModel); + assertSelectedCardUnchanged(); + + /* Case: search keywords in different fields -> 1 persons found*/ + command = FindCommand.COMMAND_WORD + " t/friends n/daniel"; + ModelHelper.setFilteredList(expectedModel, DANIEL); + assertCommandSuccess(command, expectedModel); + assertSelectedCardUnchanged(); + + /* Case: search keywords in different fields -> 0 persons found*/ + command = FindCommand.COMMAND_WORD + " a/utown t/owesMoney"; + assertCommandSuccess(command, expectedModel); + assertSelectedCardUnchanged(); } /** @@ -162,6 +191,7 @@ public void find() { * {@code AddressBookSystemTest#assertApplicationDisplaysExpected(String, String, Model)}.
* Also verifies that the status bar remains unchanged, and the command box has the default style class, and the * selected card updated accordingly, depending on {@code cardStatus}. + * * @see AddressBookSystemTest#assertApplicationDisplaysExpected(String, String, Model) */ private void assertCommandSuccess(String command, Model expectedModel) { @@ -181,6 +211,7 @@ private void assertCommandSuccess(String command, Model expectedModel) { * {@code AddressBookSystemTest#assertApplicationDisplaysExpected(String, String, Model)}.
* Also verifies that the browser url, selected card and status bar remain unchanged, and the command box has the * error style. + * * @see AddressBookSystemTest#assertApplicationDisplaysExpected(String, String, Model) */ private void assertCommandFailure(String command, String expectedResultMessage) {