From c526907e322f0868d59608595ee969ae96cdfb0a Mon Sep 17 00:00:00 2001 From: Sarah O'Reilly Date: Sun, 22 Nov 2020 21:07:33 -0800 Subject: [PATCH 1/6] Implement mock for getWord on WordOfTheDay --- .../mills/cs180a/wordui/model/SampleData.java | 7 ++++++- .../cs180a/wordui/model/SampleDataTest.java | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java b/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java index f9d8846..a085828 100644 --- a/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java +++ b/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java @@ -41,6 +41,11 @@ private static int getFrequencyFromSummary(FrequencySummary fs, int year) { } return 0; } + + @VisibleForTesting + protected static WordOfTheDay getWordOfTheDay(WordsApi wordsApi) { + return wordsApi.getWordOfTheDay(); + } // TODO: Move to spring-swagger-wordnik-client @VisibleForTesting @@ -61,7 +66,7 @@ public static void fillSampleData(ObservableList backingList) { try { client = ApiClientHelper.getApiClient(); WordsApi wordsApi = client.buildClient(WordsApi.class); - WordOfTheDay word = wordsApi.getWordOfTheDay(); + WordOfTheDay word = getWordOfTheDay(wordsApi); List definitions = word.getDefinitions(); if (definitions != null && !definitions.isEmpty()) { Object definition = definitions.get(0); diff --git a/src/test/java/edu/mills/cs180a/wordui/model/SampleDataTest.java b/src/test/java/edu/mills/cs180a/wordui/model/SampleDataTest.java index a7808ec..95d71c3 100644 --- a/src/test/java/edu/mills/cs180a/wordui/model/SampleDataTest.java +++ b/src/test/java/edu/mills/cs180a/wordui/model/SampleDataTest.java @@ -11,18 +11,25 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import edu.mills.cs180a.wordnik.client.api.WordApi; +import edu.mills.cs180a.wordnik.client.api.WordsApi; import edu.mills.cs180a.wordnik.client.model.FrequencySummary; +import edu.mills.cs180a.wordnik.client.model.WordOfTheDay; class SampleDataTest { private final WordApi mockWordApi = mock(WordApi.class); private static final Map FREQS_MAP = Map.of( "apple", makeFrequencySummary(List.of(makeMap(2000, 339), makeMap(2001, 464))), "orange", makeFrequencySummary(List.of(makeMap(2000, 774), makeMap(2001, 941)))); + + private final WordsApi mockWordsApi = mock(WordsApi.class); + private static final WordOfTheDay WORD_TODAY = makeWordOfTheDay(); @BeforeEach void setup() { when(mockWordApi.getWordFrequency(anyString(), anyString(), anyInt(), anyInt())) .thenAnswer(invocation -> FREQS_MAP.get(invocation.getArgument(0))); + when(mockWordsApi.getWordOfTheDay()) + .thenReturn(WORD_TODAY); } private static Map makeMap(int year, int count) { @@ -35,6 +42,12 @@ private static FrequencySummary makeFrequencySummary(List freqs) { when(fs.getFrequency()).thenReturn(freqs); return fs; } + + private static WordOfTheDay makeWordOfTheDay() { + WordOfTheDay wotd = mock(WordOfTheDay.class); + when(wotd.getWord()).thenReturn("banana"); + return wotd; + } @ParameterizedTest @CsvSource({"apple,2000,339", "apple,2001,464", "apple,2020,0", "orange,2000,774", @@ -42,4 +55,8 @@ private static FrequencySummary makeFrequencySummary(List freqs) { void testGetFrequencyFromSummary(String word, int year, int count) { assertEquals(count, SampleData.getFrequencyByYear(mockWordApi, word, year)); } + + void testGetWordOfTheDay() { + assertEquals("banana", SampleData.getWordOfTheDay(mockWordsApi)); + } } From e66cbeb528c55c026368688b656b592bafbd8362 Mon Sep 17 00:00:00 2001 From: Sarah O'Reilly Date: Mon, 23 Nov 2020 20:40:48 -0800 Subject: [PATCH 2/6] Implement mock for getDefinitions --- .../mills/cs180a/wordui/model/SampleDataTest.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/test/java/edu/mills/cs180a/wordui/model/SampleDataTest.java b/src/test/java/edu/mills/cs180a/wordui/model/SampleDataTest.java index 95d71c3..6ba4ecb 100644 --- a/src/test/java/edu/mills/cs180a/wordui/model/SampleDataTest.java +++ b/src/test/java/edu/mills/cs180a/wordui/model/SampleDataTest.java @@ -5,6 +5,8 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; + +import java.util.LinkedList; import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -12,6 +14,7 @@ import org.junit.jupiter.params.provider.CsvSource; import edu.mills.cs180a.wordnik.client.api.WordApi; import edu.mills.cs180a.wordnik.client.api.WordsApi; +import edu.mills.cs180a.wordnik.client.model.Definition; import edu.mills.cs180a.wordnik.client.model.FrequencySummary; import edu.mills.cs180a.wordnik.client.model.WordOfTheDay; @@ -45,7 +48,11 @@ private static FrequencySummary makeFrequencySummary(List freqs) { private static WordOfTheDay makeWordOfTheDay() { WordOfTheDay wotd = mock(WordOfTheDay.class); + List bananaDef = new LinkedList<>(); + bananaDef.add("An elongated curved fruit, which grows in bunches, and has a sweet creamy flesh and a smooth yellow skin."); + when(wotd.getWord()).thenReturn("banana"); + when(wotd.getDefinitions()).thenReturn(bananaDef); return wotd; } @@ -56,7 +63,12 @@ void testGetFrequencyFromSummary(String word, int year, int count) { assertEquals(count, SampleData.getFrequencyByYear(mockWordApi, word, year)); } - void testGetWordOfTheDay() { + void getWordOfTheDay_True_mockedWord() { assertEquals("banana", SampleData.getWordOfTheDay(mockWordsApi)); } + + void getDefinition_True_mockedDefinition() { + assertEquals("An elongated curved fruit, which grows in bunches, and has a sweet creamy flesh and a smooth yellow skin.", + SampleData.getWordOfTheDay(mockWordsApi).getDefinitions()); + } } From 8fe163a719fbf3b954db2a907555afb01ceffac1 Mon Sep 17 00:00:00 2001 From: Sarah O'Reilly Date: Mon, 23 Nov 2020 20:48:54 -0800 Subject: [PATCH 3/6] Add javadoc for fillSampleData --- .../java/edu/mills/cs180a/wordui/model/SampleData.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java b/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java index a085828..626fd13 100644 --- a/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java +++ b/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java @@ -62,6 +62,13 @@ private static WordRecord buildWordRecord(String word, Map defin definition.get("text").toString()); } + /*** + * Creates a list of standard sample words and their definitions along + * with the word of the day. Implements the wordnik API. + * + * @param backingList a list of sample word records + * @throws IOException if unable to get wordnik API key + */ public static void fillSampleData(ObservableList backingList) { try { client = ApiClientHelper.getApiClient(); From 9e76743ff167bb2c54cac0e3c9391444791241b8 Mon Sep 17 00:00:00 2001 From: Sarah O'Reilly Date: Mon, 30 Nov 2020 20:09:35 -0800 Subject: [PATCH 4/6] Fix definitions, implement addWordOfTheDay --- .classpath | 29 --- .gitignore | 62 ------ .project | 23 --- nb-configuration.xml | 18 -- nbactions.xml | 26 --- pom.xml | 134 ------------ .../mills/cs180a/wordui/FXMLController.java | 192 ------------------ .../java/edu/mills/cs180a/wordui/WordUI.java | 22 -- .../mills/cs180a/wordui/model/SampleData.java | 39 ++-- src/main/resources/fxml/Scene.fxml | 115 ----------- src/main/resources/styles/Styles.css | 3 - .../cs180a/wordui/model/SampleDataTest.java | 54 ++++- 12 files changed, 71 insertions(+), 646 deletions(-) delete mode 100644 .classpath delete mode 100644 .gitignore delete mode 100644 .project delete mode 100644 nb-configuration.xml delete mode 100644 nbactions.xml delete mode 100644 pom.xml delete mode 100644 src/main/java/edu/mills/cs180a/wordui/FXMLController.java delete mode 100644 src/main/java/edu/mills/cs180a/wordui/WordUI.java delete mode 100644 src/main/resources/fxml/Scene.fxml delete mode 100644 src/main/resources/styles/Styles.css diff --git a/.classpath b/.classpath deleted file mode 100644 index 16de989..0000000 --- a/.classpath +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 288a569..0000000 --- a/.gitignore +++ /dev/null @@ -1,62 +0,0 @@ -.metadata -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.settings/ -.loadpath -.recommenders - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# PyDev specific (Python IDE for Eclipse) -*.pydevproject - -# CDT-specific (C/C++ Development Tooling) -.cproject - -# CDT- autotools -.autotools - -# Java annotation processor (APT) -.factorypath - -# PDT-specific (PHP Development Tools) -.buildpath - -# sbteclipse plugin -.target - -# Tern plugin -.tern-project - -# TeXlipse plugin -.texlipse - -# STS (Spring Tool Suite) -.springBeans - -# Code Recommenders -.recommenders/ - -# Annotation Processing -.apt_generated/ -.apt_generated_test/ - -# Scala IDE specific (Scala & Java development for Eclipse) -.cache-main -.scala_dependencies -.worksheet - -# Uncomment this line if you wish to ignore the project description file. -# Typically, this file would be tracked if it contains build/dependency configurations: -#.project -/target/ -api-key.txt diff --git a/.project b/.project deleted file mode 100644 index 051b1b5..0000000 --- a/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - wordui - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/nb-configuration.xml b/nb-configuration.xml deleted file mode 100644 index ec4540c..0000000 --- a/nb-configuration.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - none - - diff --git a/nbactions.xml b/nbactions.xml deleted file mode 100644 index 9c9ee62..0000000 --- a/nbactions.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - run - - clean - package - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec - - - -jar "${project.build.directory}/${project.build.finalName}.jar" - - - - debug - - clean - package - org.codehaus.mojo:exec-maven-plugin:1.2.1:exec - - - -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address} -Dglass.disableGrab=true -jar "${project.build.directory}/${project.build.finalName}.jar" - true - - - diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 6e457ab..0000000 --- a/pom.xml +++ /dev/null @@ -1,134 +0,0 @@ - - - 4.0.0 - - edu.mills.cs180a.wordui - wordui - 1.0-SNAPSHOT - jar - - wordui - - - UTF-8 - org.openjfx.MainApp - UTF-8 - 15 - ${java.version} - ${java.version} - - - - - Mills College Department of Mathematics and Computer Science - - - - - CS180A site - Mills CS180A repository - http://spertus.com/cs180a - - - - - org.openjfx - javafx-controls - 11 - - - org.openjfx - javafx-fxml - 11 - - - edu.mills.cs180a - spring-swagger-wordnik-client - 0.0.2-SNAPSHOT - - - org.projectlombok - lombok - 1.18.2 - provided - - - - org.mockito - mockito-core - 3.6.0 - test - - - - com.google.guava - guava-annotations - r03 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.0 - - 11 - - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - - - java - - - - - edu.mills.cs180a.wordui.WordUI - - - - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.1.1 - - - compile - - copy-dependencies - - - ${project.build.directory}/lib - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.2.0 - - - - true - lib/ - edu.mills.cs180a.wordui.WordUI - - - - - - - - - diff --git a/src/main/java/edu/mills/cs180a/wordui/FXMLController.java b/src/main/java/edu/mills/cs180a/wordui/FXMLController.java deleted file mode 100644 index 8439ec7..0000000 --- a/src/main/java/edu/mills/cs180a/wordui/FXMLController.java +++ /dev/null @@ -1,192 +0,0 @@ -package edu.mills.cs180a.wordui; - -import java.net.URL; -import java.util.ResourceBundle; -import edu.mills.cs180a.wordui.model.SampleData; -import edu.mills.cs180a.wordui.model.WordRecord; -import edu.mills.cs180a.wordui.model.WordRecord.SortOrder; -import javafx.beans.binding.Bindings; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.StringProperty; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.collections.transformation.SortedList; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.Label; -import javafx.scene.control.ListView; -import javafx.scene.control.TextArea; -import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; - -public class FXMLController implements Initializable { - @FXML - private Label recordCountLabel; - @FXML - private TextField wordTextField; - @FXML - private TextField frequencyTextField; - @FXML - private TextArea definitionTextArea; - @FXML - private Button removeButton; - @FXML - private Button createButton; - @FXML - private Button updateButton; - @FXML - private ListView listView; - @FXML - private ChoiceBox sortChoiceBox; - - private final ObservableList wordRecordList = FXCollections.observableArrayList(); - - private WordRecord selectedWordRecord; - private final BooleanProperty modifiedProperty = new SimpleBooleanProperty(false); - private final BooleanProperty freqValidProperty = new SimpleBooleanProperty(false); - private ChangeListener wordRecordChangeListener = new WordRecordChangeListener(); - private ChangeListener sortOrderChangeListener = - new SortOrderChangeListener(); - - // Called when the user selects or unselects a WordRecord. - private class WordRecordChangeListener implements ChangeListener { - @Override - public void changed(ObservableValue observable, WordRecord oldValue, - WordRecord newValue) { - // newValue can be null if nothing is selected. - System.out.println("Selected item: " + newValue); - selectedWordRecord = newValue; - modifiedProperty.set(false); - if (newValue != null) { - wordTextField.setText(selectedWordRecord.getWord()); - frequencyTextField.setText(Integer.toString(selectedWordRecord.getFrequency())); - freqValidProperty.set(isValidFrequency(frequencyTextField.textProperty())); - definitionTextArea.setText(selectedWordRecord.getDefinition()); - } else { - wordTextField.setText(""); - frequencyTextField.setText(""); - freqValidProperty.set(false); - definitionTextArea.setText(""); - } - } - } - - private class SortOrderChangeListener implements ChangeListener { - @Override - public void changed(ObservableValue observable, SortOrder oldValue, - SortOrder newValue) { - setSortOrder(newValue); - } - } - - private void setSortOrder(WordRecord.SortOrder newOrder) { - SortedList sortedList = new SortedList<>(wordRecordList); - sortedList.setComparator(newOrder.getComparator()); - listView.setItems(sortedList); - } - - @Override - public void initialize(URL url, ResourceBundle rb) { - addListeners(); - setupListView(); - configureButtons(); - populateChoiceBox(); - } - - private void setupListView() { - // Initialize the list. - SampleData.fillSampleData(wordRecordList); - - // Sort list alphabetically. - setSortOrder(WordRecord.SortOrder.ALPHABETICALLY_FORWARD); - - // Set up the record count. This must occur after listView is populated. - recordCountLabel.textProperty().bind(Bindings.size(listView.getItems()).asString()); - - // Pre-select the first item. - listView.getSelectionModel().selectFirst(); - } - - private void populateChoiceBox() { - sortChoiceBox.setItems(FXCollections.observableArrayList( - WordRecord.SortOrder.values())); - sortChoiceBox.setValue(WordRecord.SortOrder.ALPHABETICALLY_FORWARD); - } - - private void configureButtons() { - // Disable the Remove button if nothing is selected in the ListView control. - removeButton.disableProperty() - .bind(listView.getSelectionModel().selectedItemProperty().isNull()); - - // Disable the Update button if nothing is selected, no modifications have - // been made, or any field is empty or invalid. - updateButton.disableProperty() - .bind(listView.getSelectionModel().selectedItemProperty().isNull() - .or(modifiedProperty.not()) - .or(freqValidProperty.not()) - .or(wordTextField.textProperty().isEmpty()) - .or(definitionTextArea.textProperty().isEmpty())); - - // TODO: Disable the Create button if an existing entry is selected or any - // field is empty or invalid. - } - - // A frequency is valid if it is an integer and is at least 0. - private boolean isValidFrequency(StringProperty sp) { - try { - int value = Integer.parseInt(sp.get()); - return value >= 0; - } catch (NumberFormatException e) { - return false; - } - } - - private void addListeners() { - listView.getSelectionModel().selectedItemProperty().addListener(wordRecordChangeListener); - sortChoiceBox.getSelectionModel().selectedItemProperty() - .addListener(sortOrderChangeListener); - } - - @FXML - private void handleKeyAction(KeyEvent keyEvent) { - modifiedProperty.set(true); - freqValidProperty.set(isValidFrequency(frequencyTextField.textProperty())); - } - - @FXML - private void createButtonAction(ActionEvent actionEvent) { - System.out.println("Create"); - WordRecord wordRecord = - new WordRecord( - wordTextField.getText(), - Integer.parseInt(frequencyTextField.getText()), - definitionTextArea.getText()); - wordRecordList.add(wordRecord); - listView.getSelectionModel().select(wordRecord); // select the new item - } - - @FXML - private void removeButtonAction(ActionEvent actionEvent) { - System.out.println("Remove " + selectedWordRecord); - wordRecordList.remove(selectedWordRecord); - } - - @FXML - private void updateButtonAction(ActionEvent actionEvent) { - System.out.println("Update " + selectedWordRecord); - WordRecord entry = listView.getSelectionModel().getSelectedItem(); - listView.getSelectionModel().selectedItemProperty() - .removeListener(wordRecordChangeListener); - entry.setWord(wordTextField.getText()); - entry.setFrequency(Integer.parseInt(frequencyTextField.getText())); - entry.setDefinition(definitionTextArea.getText()); - listView.getSelectionModel().selectedItemProperty().addListener(wordRecordChangeListener); - modifiedProperty.set(false); - } -} diff --git a/src/main/java/edu/mills/cs180a/wordui/WordUI.java b/src/main/java/edu/mills/cs180a/wordui/WordUI.java deleted file mode 100644 index 813a62e..0000000 --- a/src/main/java/edu/mills/cs180a/wordui/WordUI.java +++ /dev/null @@ -1,22 +0,0 @@ -package edu.mills.cs180a.wordui; - -import javafx.application.Application; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.stage.Stage; - -public class WordUI extends Application { - @Override - public void start(Stage stage) throws Exception { - Parent root = FXMLLoader.load(getClass().getResource("/fxml/Scene.fxml")); - Scene scene = new Scene(root); - stage.setTitle("Word UI Example"); - stage.setScene(scene); - stage.show(); - } - - public static void main(String[] args) { - launch(args); - } -} diff --git a/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java b/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java index 626fd13..95fc197 100644 --- a/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java +++ b/src/main/java/edu/mills/cs180a/wordui/model/SampleData.java @@ -18,7 +18,8 @@ public class SampleData { @VisibleForTesting protected static final String FREQ_YEAR_KEY = "year"; private static final int FREQ_YEAR = 2012; - private static ApiClient client; // set in fillSampleData() + @VisibleForTesting + protected static ApiClient client; // set in fillSampleData() private static int getFrequencyFromSummary(FrequencySummary fs, int year) { List freqObjects = fs.getFrequency(); @@ -54,35 +55,49 @@ protected static int getFrequencyByYear(WordApi wordApi, String word, int year) return getFrequencyFromSummary(fs, year); } - private static WordRecord buildWordRecord(String word, Map definition) { + @VisibleForTesting + protected static WordRecord buildWordRecord(String word, Map definition) { WordApi wordApi = client.buildClient(WordApi.class); return new WordRecord( word, getFrequencyByYear(wordApi, word, FREQ_YEAR), definition.get("text").toString()); } + + /*** + * Adds the current word of the day to a given list of words. + * + * @param backingList a list to which the word of the day will be added + * @param wordsApi the API used to get today's word of the day + */ + @VisibleForTesting + protected static void addWordOfTheDay(List backingList, WordOfTheDay word) { + List definitions = word.getDefinitions(); + if(definitions != null && !definitions.isEmpty()) { + Object definition = definitions.get(0); + if (definition instanceof Map) { + @SuppressWarnings("unchecked") + Map definitionAsMap = (Map) definition; + backingList.add(buildWordRecord(word.getWord(), definitionAsMap)); + } + } + } /*** * Creates a list of standard sample words and their definitions along * with the word of the day. Implements the wordnik API. * * @param backingList a list of sample word records - * @throws IOException if unable to get wordnik API key */ public static void fillSampleData(ObservableList backingList) { try { client = ApiClientHelper.getApiClient(); WordsApi wordsApi = client.buildClient(WordsApi.class); WordOfTheDay word = getWordOfTheDay(wordsApi); - List definitions = word.getDefinitions(); - if (definitions != null && !definitions.isEmpty()) { - Object definition = definitions.get(0); - if (definition instanceof Map) { - @SuppressWarnings("unchecked") - Map definitionAsMap = (Map) definition; - backingList.add(buildWordRecord(word.getWord(), definitionAsMap)); - } - } + System.out.println("word (wotd): " + word.getWord()); + System.out.println("defs: " + word.getDefinitions()); + addWordOfTheDay(backingList, word); + } catch (IOException e) { System.err.println("Unable to get API key."); } diff --git a/src/main/resources/fxml/Scene.fxml b/src/main/resources/fxml/Scene.fxml deleted file mode 100644 index 3b2576e..0000000 --- a/src/main/resources/fxml/Scene.fxml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -