001package edu.pdx.cs.joy.grader.poa; 002 003import com.google.common.eventbus.Subscribe; 004import edu.pdx.cs.joy.grader.gradebook.Assignment; 005import edu.pdx.cs.joy.grader.gradebook.Grade; 006import edu.pdx.cs.joy.grader.gradebook.GradeBook; 007import edu.pdx.cs.joy.grader.gradebook.Student; 008import org.junit.jupiter.api.BeforeEach; 009import org.junit.jupiter.api.Test; 010import org.mockito.ArgumentCaptor; 011import org.mockito.InOrder; 012 013import java.time.LocalDateTime; 014 015import static org.hamcrest.MatcherAssert.assertThat; 016import static org.hamcrest.Matchers.equalTo; 017import static org.hamcrest.Matchers.nullValue; 018import static org.mockito.ArgumentMatchers.eq; 019import static org.mockito.Mockito.*; 020 021public class POAGradePresenterTest extends POASubmissionTestCase { 022 023 private final double scoreForPOAWithGrade = 1.5; 024 private POAGradeView view; 025 private POASubmission submission; 026 private Student student; 027 private Assignment assignment; 028 private GradeBook book; 029 private POAGradePresenter presenter; 030 private POASubmission submissionForPOAWithGrade; 031 private Assignment assignmentWithPOAGrade; 032 033 @BeforeEach 034 @Override 035 public void setUp() { 036 super.setUp(); 037 038 this.view = mock(POAGradeView.class); 039 040 presenter = new POAGradePresenter(this.bus, this.view); 041 submission = createPOASubmission("Subject", "Submitter", LocalDateTime.now()); 042 student = new Student("id"); 043 assignment = new Assignment("assignment", 1.0); 044 045 book = new GradeBook("POAGradePresenterTest"); 046 book.addAssignment(assignment); 047 book.addStudent(student); 048 049 submissionForPOAWithGrade = createPOASubmission("POA for Project with Grade", "Submitter", LocalDateTime.now()); 050 assignmentWithPOAGrade = new Assignment("assignmentWithPOAGrade", 2.0); 051 book.addAssignment(assignmentWithPOAGrade); 052 student.setGrade(assignmentWithPOAGrade, scoreForPOAWithGrade); 053 } 054 055 @Test 056 public void onlySubmissionSelectedDisablesView() { 057 this.bus.post(new POASubmissionSelected(submission)); 058 059 verifyIsLate(false); 060 verify(this.view).setIsEnabled(eq(false)); 061 } 062 063 private void verifyIsLate(boolean isLate) { 064 verifyIsLate(1, isLate); 065 } 066 067 @Test 068 public void onlyStudentSelectedDisablesView() { 069 this.bus.post(new StudentSelectedEvent(student)); 070 071 verifyIsLate(false); 072 verify(this.view).setIsEnabled(eq(false)); 073 } 074 075 @Test 076 public void onlyAssignmentSelectedDisablesView() { 077 this.bus.post(new GradeBookLoaded(book)); 078 this.bus.post(new AssignmentSelectedEvent(assignment)); 079 080 verifyIsLate(false); 081 verify(this.view).setIsEnabled(eq(false)); 082 } 083 084 @Test 085 public void submissionStudentAndAssignmentEnablesView() { 086 postEventsToBus(); 087 088 verify(this.view).setIsEnabled(true); 089 } 090 091 @Test 092 public void lateSubmissionIsLateInView() { 093 assignment.setDueDate(LocalDateTime.now().minusDays(5)); 094 095 postEventsToBus(); 096 097 verify(this.view).setIsEnabled(true); 098 verifyIsLate(true); 099 } 100 101 @Test 102 public void onTimeSubmissionIsNotLateInView() { 103 assignment.setDueDate(LocalDateTime.now().plusDays(5)); 104 105 postEventsToBus(); 106 107 verify(this.view).setIsEnabled(true); 108 verifyIsLate(3, false); 109 } 110 111 @Test 112 public void assignmentWithNoDueDateIsNotLate() { 113 assignment.setDueDate(null); 114 115 postEventsToBus(); 116 117 verify(this.view).setIsEnabled(true); 118 int wantedNumberOfInvocations = 3; 119 verifyIsLate(wantedNumberOfInvocations, false); 120 } 121 122 private void verifyIsLate(int wantedNumberOfInvocations, boolean isLate) { 123 verify(this.view, times(wantedNumberOfInvocations)).setIsLate(eq(isLate)); 124 assertThat(this.presenter.isLate(), equalTo(isLate)); 125 } 126 127 @Test 128 public void latePOAIsStoredInPresenterWhenMarkedLateInView() { 129 ArgumentCaptor<POAGradeView.IsLateHandler> handler = ArgumentCaptor.forClass(POAGradeView.IsLateHandler.class); 130 verify(this.view).addIsLateHandler(handler.capture()); 131 132 assignment.setDueDate(LocalDateTime.now().plusDays(5)); 133 134 postEventsToBus(); 135 136 verify(this.view).setIsEnabled(true); 137 verifyIsLate(3, false); 138 139 handler.getValue().setIsLate(true); 140 verifyIsLate(true); 141 142 handler.getValue().setIsLate(false); 143 verifyIsLate(4, false); 144 } 145 146 @Test 147 public void assignmentTotalPointsDisplayedInViewWhenAssignmentSelected() { 148 double points = 2.75; 149 assignment.setPoints(points); 150 this.bus.post(new GradeBookLoaded(book)); 151 this.bus.post(new AssignmentSelectedEvent(assignment)); 152 153 verify(this.view).setTotalPoints("2.75"); 154 } 155 156 @Test 157 public void scoreInViewIsInErrorWhenValueIsNotAnInteger() { 158 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 159 verify(view).addScoreValueHandler(handler.capture()); 160 161 handler.getValue().scoreValue("Not an Integer"); 162 163 verify(this.view).setErrorInScore(true); 164 assertThat(presenter.getScore(), nullValue()); 165 } 166 167 @Test 168 public void setPresentersScoreFromView() { 169 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 170 verify(view).addScoreValueHandler(handler.capture()); 171 172 this.bus.post(new GradeBookLoaded(book)); 173 this.bus.post(new AssignmentSelectedEvent(assignment.setPoints(2.0d))); 174 reset(this.view); 175 176 handler.getValue().scoreValue("1.75"); 177 178 verify(this.view).setErrorInScore(false); 179 assertThat(presenter.getScore(), equalTo(1.75)); 180 } 181 182 @Test 183 public void negativeScoreIsAnError() { 184 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 185 verify(view).addScoreValueHandler(handler.capture()); 186 187 handler.getValue().scoreValue("-4.3"); 188 189 verify(this.view).setErrorInScore(true); 190 assertThat(presenter.getScore(), nullValue()); 191 } 192 193 @Test 194 public void errorStateShouldBeClearedWhenNewSubmissionIsSelected() { 195 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 196 verify(view).addScoreValueHandler(handler.capture()); 197 198 handler.getValue().scoreValue("-4.3"); 199 200 verify(this.view).setErrorInScore(true); 201 202 this.bus.post(new POASubmissionSelected(submission)); 203 204 verify(this.view).setErrorInScore(false); 205 } 206 207 @Test 208 public void scoreShouldBeClearedWhenNewSubmissionIsSelected() { 209 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 210 verify(view).addScoreValueHandler(handler.capture()); 211 212 handler.getValue().scoreValue("-4.3"); 213 214 this.bus.post(new POASubmissionSelected(submission)); 215 216 verify(this.view).setScore(""); 217 assertThat(this.presenter.getScore(), nullValue()); 218 } 219 220 @Test 221 public void scoreAndErrorShouldBeClearedWhenNewStudentIsSelected() { 222 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 223 verify(view).addScoreValueHandler(handler.capture()); 224 225 handler.getValue().scoreValue("-4.3"); 226 verify(this.view).setErrorInScore(true); 227 228 this.bus.post(new StudentSelectedEvent(student)); 229 230 verify(this.view).setErrorInScore(false); 231 verify(this.view, times(2)).setScore(""); 232 assertThat(this.presenter.getScore(), nullValue()); 233 } 234 235 @Test 236 public void scoreAndErrorShouldBeClearedWhenNewAssignmentIsSelected() { 237 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 238 verify(view).addScoreValueHandler(handler.capture()); 239 240 handler.getValue().scoreValue("-4.3"); 241 verify(this.view).setErrorInScore(true); 242 243 this.bus.post(new GradeBookLoaded(book)); 244 this.bus.post(new AssignmentSelectedEvent(assignment)); 245 246 verify(this.view, times(2)).setErrorInScore(false); 247 verify(this.view).setScore(POAGradePresenter.formatTotalPoints(assignment.getPoints())); 248 assertThat(this.presenter.getScore(), equalTo(assignment.getPoints())); 249 } 250 251 @Test 252 public void scoreDefaultsToTotalPoints() { 253 postEventsToBus(); 254 255 verify(this.view).setScore(POAGradePresenter.formatTotalPoints(assignment.getPoints())); 256 } 257 258 private void postEventsToBus() { 259 this.bus.post(new GradeBookLoaded(book)); 260 this.bus.post(new POASubmissionSelected(submission)); 261 this.bus.post(new StudentSelectedEvent(student)); 262 this.bus.post(new AssignmentSelectedEvent(assignment)); 263 } 264 265 @Test 266 public void aScoreOfEmptyStringShouldNotBeAnError() { 267 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 268 verify(view).addScoreValueHandler(handler.capture()); 269 270 handler.getValue().scoreValue(""); 271 272 verify(this.view).setErrorInScore(false); 273 assertThat(presenter.getScore(), nullValue()); 274 } 275 276 @Test 277 public void aScoreThatIsGreaterThanTheTotalPointsShouldBeAnError() { 278 ArgumentCaptor<POAGradeView.ScoreValueHandler> handler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 279 verify(view).addScoreValueHandler(handler.capture()); 280 281 this.bus.post(new GradeBookLoaded(book)); 282 this.bus.post(new AssignmentSelectedEvent(assignment)); 283 284 handler.getValue().scoreValue(String.valueOf(assignment.getPoints() + 1.0d)); 285 286 verify(this.view).setErrorInScore(true); 287 assertThat(presenter.getScore(), nullValue()); 288 } 289 290 @Test 291 public void scoreDefaultsToCurrentGrade() { 292 double score = 0.75; 293 student.setGrade(assignment, new Grade(assignment, score)); 294 295 postEventsToBus(); 296 297 verify(this.view).setScore(POAGradePresenter.formatTotalPoints(score)); 298 } 299 300 @Test 301 public void scoreDefaultsToTotalPointsWhenNoStudentIsSelected() { 302 this.bus.post(new GradeBookLoaded(book)); 303 this.bus.post(new AssignmentSelectedEvent(assignment)); 304 305 verify(this.view).setScore(POAGradePresenter.formatTotalPoints(assignment.getPoints())); 306 } 307 308 @Test 309 public void scoreHasNotBeenRecordedWhenNoStudentIsSelected() { 310 this.bus.post(new GradeBookLoaded(book)); 311 this.bus.post(new AssignmentSelectedEvent(assignment)); 312 313 verify(this.view, times(2)).setScoreHasBeenRecorded(false); 314 } 315 316 @Test 317 public void scoreHasNotBeenRecordedWhenThereIsNoGradeInGradeBook() { 318 assertThat(student.getGrade(assignment), nullValue()); 319 320 postEventsToBus(); 321 322 verify(this.view, times(5)).setScoreHasBeenRecorded(false); 323 } 324 325 @Test 326 public void scoreHasBeenRecordedForSelectedStudentWhenItIsInGradeBook() { 327 double score = 0.75; 328 student.setGrade(assignment, new Grade(assignment, score)); 329 330 postEventsToBus(); 331 332 verify(this.view).setScoreHasBeenRecorded(true); 333 } 334 335 @Test 336 public void scoreHasBeenRecordedShouldBeFalseWhenNewSubmissionIsSelected() { 337 this.bus.post(new POASubmissionSelected(submission)); 338 339 verify(this.view).setScoreHasBeenRecorded(false); 340 } 341 342 @Test 343 public void scoreHasBeenRecordedShouldBeFalseWhenNewStudentIsSelected() { 344 this.bus.post(new StudentSelectedEvent(student)); 345 346 verify(this.view, times(2)).setScoreHasBeenRecorded(false); 347 } 348 349 @Test 350 public void recordingGradeInViewPublishesRecordGradeMessage() { 351 ArgumentCaptor<POAGradeView.ScoreValueHandler> scoreHandler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 352 verify(this.view).addScoreValueHandler(scoreHandler.capture()); 353 354 ArgumentCaptor<POAGradeView.RecordGradeHandler> recordGrade = ArgumentCaptor.forClass(POAGradeView.RecordGradeHandler.class); 355 verify(this.view).addRecordGradeHandler(recordGrade.capture()); 356 357 RecordGradeEventHandler eventHandler = mock(RecordGradeEventHandler.class); 358 this.bus.register(eventHandler); 359 360 this.assignment.setDueDate(LocalDateTime.now().minusDays(5)); 361 postEventsToBus(); 362 363 double score = 0.75; 364 scoreHandler.getValue().scoreValue(POAGradePresenter.formatTotalPoints(score)); 365 recordGrade.getValue().recordGrade(); 366 367 ArgumentCaptor<RecordGradeEvent> eventCaptor = ArgumentCaptor.forClass(RecordGradeEvent.class); 368 verify(eventHandler).handleRecordGradeEvent(eventCaptor.capture()); 369 370 RecordGradeEvent event = eventCaptor.getValue(); 371 assertThat(event.getScore(), equalTo(score)); 372 assertThat(event.getStudent(), equalTo(this.student)); 373 assertThat(event.getAssignment(), equalTo(this.assignment)); 374 assertThat(event.isLate(), equalTo(true)); 375 376 verify(this.view).setScoreHasBeenRecorded(true); 377 } 378 379 @Test 380 public void recordingGradeInViewPublishesDisplayNextPOAMessage() { 381 ArgumentCaptor<POAGradeView.ScoreValueHandler> scoreHandler = ArgumentCaptor.forClass(POAGradeView.ScoreValueHandler.class); 382 verify(this.view).addScoreValueHandler(scoreHandler.capture()); 383 384 ArgumentCaptor<POAGradeView.RecordGradeHandler> recordGrade = ArgumentCaptor.forClass(POAGradeView.RecordGradeHandler.class); 385 verify(this.view).addRecordGradeHandler(recordGrade.capture()); 386 387 DisplayNextPOAEventHandler eventHandler = mock(DisplayNextPOAEventHandler.class); 388 this.bus.register(eventHandler); 389 390 this.assignment.setDueDate(LocalDateTime.now().minusDays(5)); 391 postEventsToBus(); 392 393 double score = 0.75; 394 scoreHandler.getValue().scoreValue(POAGradePresenter.formatTotalPoints(score)); 395 recordGrade.getValue().recordGrade(); 396 397 ArgumentCaptor<SelectNextPOAEvent> eventCaptor = ArgumentCaptor.forClass(SelectNextPOAEvent.class); 398 verify(eventHandler).handleDisplayNextPOAEvent(eventCaptor.capture()); 399 } 400 401 private interface RecordGradeEventHandler { 402 @Subscribe 403 void handleRecordGradeEvent(RecordGradeEvent event); 404 } 405 406 @Test 407 public void currentGradeIsDisplayedWhenGradedSubmissionIsSelected() { 408 this.bus.post(new GradeBookLoaded(book)); 409 this.bus.post(new POASubmissionSelected(submissionForPOAWithGrade)); 410 verify(this.view).setScore(""); 411 412 this.bus.post(new StudentSelectedEvent(student)); 413 verify(this.view, times(3)).setScore(""); 414 415 this.bus.post(new AssignmentSelectedEvent(assignmentWithPOAGrade)); 416 417 InOrder inOrder = inOrder(this.view); 418 inOrder.verify(this.view).setScore(POAGradePresenter.formatTotalPoints(scoreForPOAWithGrade)); 419 } 420 421 @Test 422 public void currentGradeIsDisplayedWhenStudentIsSelected() { 423 this.bus.post(new GradeBookLoaded(book)); 424 this.bus.post(new POASubmissionSelected(submissionForPOAWithGrade)); 425 verify(this.view, times(1)).setScore(""); 426 427 this.bus.post(new StudentSelectedEvent(student)); 428 verify(this.view, times(3)).setScore(""); 429 430 this.bus.post(new AssignmentSelectedEvent(assignmentWithPOAGrade)); 431 432 InOrder inOrder = inOrder(this.view); 433 inOrder.verify(this.view).setScore(POAGradePresenter.formatTotalPoints(scoreForPOAWithGrade)); 434 435 this.bus.post(new StudentSelectedEvent(student)); 436 inOrder.verify(this.view).setScore(POAGradePresenter.formatTotalPoints(scoreForPOAWithGrade)); 437 } 438 439 @Test 440 public void unknownStudentClearsUI() { 441 postEventsToBus(); 442 verify(this.view, times(4)).setScore(""); 443 verifyIsLate(3, false); 444 445 this.bus.post(new StudentSelectedEvent(null)); 446 447 verify(this.view, times(5)).setScore(""); 448 verifyIsLate(4, false); 449 } 450 451 private interface DisplayNextPOAEventHandler { 452 @Subscribe 453 void handleDisplayNextPOAEvent(SelectNextPOAEvent event); 454 } 455}