From d45ebf4eece34c5a1634be3a76ebb4a88caa5f7d Mon Sep 17 00:00:00 2001 From: Matt Young Date: Tue, 9 Jul 2024 14:36:04 -0500 Subject: [PATCH] Cleanup and add test for seating status screen --- .../Tabulation/SeatAuditionController.php | 17 ++ .../Tabulation/SeatingStatusController.php | 2 +- app/Models/Audition.php | 2 +- routes/tabulation.php | 20 ++- tests/Feature/Seating/indexTest.php | 149 ++++++++++++++++++ 5 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 app/Http/Controllers/Tabulation/SeatAuditionController.php create mode 100644 tests/Feature/Seating/indexTest.php diff --git a/app/Http/Controllers/Tabulation/SeatAuditionController.php b/app/Http/Controllers/Tabulation/SeatAuditionController.php new file mode 100644 index 0000000..8eb243e --- /dev/null +++ b/app/Http/Controllers/Tabulation/SeatAuditionController.php @@ -0,0 +1,17 @@ +withCount(['entries', 'unscoredEntries'])->with('flags')->get(); $auditionData = []; foreach ($auditions as $audition) { - $auditionData[] = [ + $auditionData[$audition->id] = [ 'id' => $audition->id, 'name' => $audition->name, 'scoredEntriesCount' => $audition->entries_count - $audition->unscored_entries_count, diff --git a/app/Models/Audition.php b/app/Models/Audition.php index 9553d52..9376bc4 100644 --- a/app/Models/Audition.php +++ b/app/Models/Audition.php @@ -29,7 +29,7 @@ class Audition extends Model return $this->hasMany(Entry::class); } - public function unscoredEntries() + public function unscoredEntries(): HasMany { return $this->hasMany(Entry::class) ->whereDoesntHave('scoreSheets'); diff --git a/routes/tabulation.php b/routes/tabulation.php index a7118dd..85a39f3 100644 --- a/routes/tabulation.php +++ b/routes/tabulation.php @@ -1,23 +1,30 @@ group(function () { // Score Management - Route::prefix('scores/')->controller(\App\Http\Controllers\Tabulation\ScoreController::class)->group(function () { + Route::prefix('scores/')->controller(ScoreController::class)->group(function () { Route::get('/choose_entry', 'chooseEntry')->name('scores.chooseEntry'); Route::get('/entry', 'entryScoreSheet')->name('scores.entryScoreSheet'); Route::post('/entry/{entry}', 'saveEntryScoreSheet')->name('scores.saveEntryScoreSheet'); Route::delete('/{score}', - [\App\Http\Controllers\Tabulation\ScoreController::class, 'destroyScore'])->name('scores.destroy'); + [ScoreController::class, 'destroyScore'])->name('scores.destroy'); }); // Entry Flagging - Route::prefix('entry-flags/')->controller(\App\Http\Controllers\Tabulation\EntryFlagController::class)->group(function ( + Route::prefix('entry-flags/')->controller(EntryFlagController::class)->group(function ( ) { Route::get('/choose_no_show', 'noShowSelect')->name('entry-flags.noShowSelect'); Route::get('/propose-no-show', 'noShowConfirm')->name('entry-flags.confirmNoShow'); @@ -27,11 +34,12 @@ Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function () // Seating Routes Route::prefix('seating/')->group(function () { - Route::get('/', App\Http\Controllers\Tabulation\SeatingStatusController::class)->name('seating.status'); + Route::get('/', SeatingStatusController::class)->name('seating.status'); + Route::get('/{audition}', SeatAuditionController::class)->name('seating.audition'); }); // Generic Tabulation Routes (TO BE REPLACED) - Route::prefix('tabulation/')->controller(\App\Http\Controllers\Tabulation\TabulationController::class)->group(function ( + Route::prefix('tabulation/')->controller(TabulationController::class)->group(function ( ) { Route::get('/status', 'status')->name('tabulation.status'); Route::match(['get', 'post'], '/auditions/{audition}', 'auditionSeating')->name('tabulation.audition.seat'); @@ -40,7 +48,7 @@ Route::middleware(['auth', 'verified', CheckIfCanTab::class])->group(function () }); // Advancement Routes - Route::prefix('advancement/')->controller(\App\Http\Controllers\Tabulation\AdvancementController::class)->group(function ( + Route::prefix('advancement/')->controller(AdvancementController::class)->group(function ( ) { Route::get('/status', 'status')->name('advancement.status'); Route::get('/{audition}', 'ranking')->name('advancement.ranking'); diff --git a/tests/Feature/Seating/indexTest.php b/tests/Feature/Seating/indexTest.php new file mode 100644 index 0000000..a05a09a --- /dev/null +++ b/tests/Feature/Seating/indexTest.php @@ -0,0 +1,149 @@ +assertRedirect(route('home')); + actAsNormal(); + get(route('seating.status')) + ->assertRedirect(route('dashboard')) + ->assertSessionHas('error', 'You are not authorized to perform this action'); +}); +it('responds to an admin', function () { + // Arrange + actAsAdmin(); + // Act & Assert + get(route('seating.status')) + ->assertOk(); +}); +it('responds to a tabulator', function () { + // Arrange + actAsTab(); + // Act & Assert + get(route('seating.status')) + ->assertOk(); +}); +it('sends the view a collection of audition data that includes data needed by the view', function () { + // Arrange + $auditions = Audition::factory()->count(5)->create(); + actAsAdmin(); + // Act + $response = get(route('seating.status')); + // Assert + foreach ($auditions as $audition) { + $response->assertOk() + ->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['id'] === $audition->id; + }); + $response->assertOk() + ->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['name'] === $audition->name; + }); + } +}); +it('has correct count info for an audition with 5 entries none scored', function () { + $audition = Audition::factory()->create(); + Entry::factory()->count(5)->create(['audition_id' => $audition->id]); + + actAsAdmin(); + $response = get(route('seating.status')); + $response->assertOk(); + + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoredEntriesCount'] === 0; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['totalEntriesCount'] === 5; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoredPercentage'] === 0; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoringComplete'] === false; + }); +}); +it('has correct count info for an audition with 8 entries 2 scored', function () { + $judge = User::factory()->create(); + + $audition = Audition::factory()->create(); + $entries = Entry::factory()->count(2)->create(['audition_id' => $audition->id]); + $entries->each(fn ($entry) => ScoreSheet::create([ + 'user_id' => $judge->id, + 'entry_id' => $entry->id, + 'subscores' => 7, + ])); + Entry::factory()->count(6)->create(['audition_id' => $audition->id]); + + actAsAdmin(); + $response = get(route('seating.status')); + $response->assertOk(); + + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoredEntriesCount'] === 2; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['totalEntriesCount'] === 8; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoredPercentage'] == 25; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoringComplete'] === false; + }); +}); + +it('has correct count info for an audition with 1 entries 1 scored', function () { + $judge = User::factory()->create(); + + $audition = Audition::factory()->create(); + $entry = Entry::factory()->create(['audition_id' => $audition->id]); + ScoreSheet::create([ + 'user_id' => $judge->id, + 'entry_id' => $entry->id, + 'subscores' => 3, + ]); + + actAsAdmin(); + $response = get(route('seating.status')); + $response->assertOk(); + + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoredEntriesCount'] === 1; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['totalEntriesCount'] === 1; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoredPercentage'] == 100; + }); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['scoringComplete'] === true; + }); +}); + +it('correctly shows a flag when the audition is flagged as seated', function () { + $audition = Audition::factory()->create(); + + actAsAdmin(); + $response = get(route('seating.status')); + $response->assertOk(); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['seatsPublished'] === false; + }); + $audition->addFlag('seats_published'); + $response = get(route('seating.status')); + $response->assertOk(); + $response->assertViewHas('auditionData', function ($viewAuditionData) use ($audition) { + return $viewAuditionData[$audition->id]['seatsPublished'] === true; + }); + +});