AdminEnsembles page tests

This commit is contained in:
Matt Young 2024-07-05 00:12:14 -05:00
parent 916baa2c93
commit 3a93aa0ea2
5 changed files with 173 additions and 26 deletions

View File

@ -8,7 +8,7 @@ use App\Models\Event;
use App\Models\SeatingLimit; use App\Models\SeatingLimit;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use function redirect; use function redirect;
class EnsembleController extends Controller class EnsembleController extends Controller
@ -16,16 +16,19 @@ class EnsembleController extends Controller
public function index() public function index()
{ {
$events = Event::with('ensembles')->get(); $events = Event::with('ensembles')->get();
return view('admin.ensembles.index', compact('events')); return view('admin.ensembles.index', compact('events'));
} }
public function store(Request $request) public function store(Request $request)
{ {
if(! Auth::user()->is_admin) abort(403); if (! Auth::user()->is_admin) {
abort(403);
}
request()->validate([ request()->validate([
'name' => 'required', 'name' => 'required',
'code' => ['required', 'max:6'], 'code' => ['required', 'max:6'],
'event_id' => ['required','exists:events,id'] 'event_id' => ['required', 'exists:events,id'],
]); ]);
Ensemble::create([ Ensemble::create([
@ -39,23 +42,26 @@ class EnsembleController extends Controller
public function destroy(Request $request, Ensemble $ensemble) public function destroy(Request $request, Ensemble $ensemble)
{ {
if(! Auth::user()->is_admin) abort(403); if ($ensemble->seats->count() > 0) {
return redirect()->route('admin.ensembles.index')->with('error', 'Ensemble has students seated and cannot be deleted');
}
$ensemble->delete(); $ensemble->delete();
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble deleted successfully'); return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble deleted successfully');
} }
public function updateEnsemble(Request $request, Ensemble $ensemble) public function updateEnsemble(Request $request, Ensemble $ensemble)
{ {
if(! Auth::user()->is_admin) abort(403);
request()->validate([ request()->validate([
'name' => 'required', 'name' => 'required',
'code' => 'required|max:6' 'code' => 'required|max:6',
]); ]);
$ensemble->update([ $ensemble->update([
'name' => request('name'), 'name' => request('name'),
'code' => request('code') 'code' => request('code'),
]); ]);
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble updated successfully'); return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble updated successfully');
} }
@ -65,6 +71,7 @@ class EnsembleController extends Controller
if ($ensemble->exists()) { if ($ensemble->exists()) {
$ensemble->load('seatingLimits'); $ensemble->load('seatingLimits');
} }
return view('admin.ensembles.seatingLimits', compact('ensemble', 'ensembles')); return view('admin.ensembles.seatingLimits', compact('ensemble', 'ensembles'));
} }
@ -72,26 +79,32 @@ class EnsembleController extends Controller
{ {
$request->validate([ $request->validate([
'audition' => 'required', 'audition' => 'required',
'audition.*' => ['integer','min:0'] 'audition.*' => ['integer', 'min:0'],
]); ]);
foreach ($ensemble->auditions as $audition) { foreach ($ensemble->auditions as $audition) {
SeatingLimit::upsert( SeatingLimit::upsert(
[[ [
[
'ensemble_id' => $ensemble->id, 'ensemble_id' => $ensemble->id,
'audition_id' => $audition->id, 'audition_id' => $audition->id,
'maximum_accepted' => $request->audition[$audition->id] 'maximum_accepted' => $request->audition[$audition->id],
]], ],
],
uniqueBy: ['ensemble_id', 'audition_id'], uniqueBy: ['ensemble_id', 'audition_id'],
update: ['maximum_accepted'] update: ['maximum_accepted']
); );
} }
return redirect()->route('admin.ensembles.seatingLimits')->with('success', 'Seating limits set for ' . $ensemble->name);
return redirect()->route('admin.ensembles.seatingLimits')->with('success',
'Seating limits set for '.$ensemble->name);
} }
public function updateEnsembleRank(Request $request) public function updateEnsembleRank(Request $request)
{ {
if(! Auth::user()->is_admin) abort(403); if (! Auth::user()->is_admin) {
abort(403);
}
$order = $request->input('order'); $order = $request->input('order');
$eventId = $request->input('event_id'); $eventId = $request->input('event_id');

View File

@ -15,7 +15,7 @@
</thead> </thead>
<x-table.body> <x-table.body>
@foreach($event->ensembles as $ensemble) @foreach($event->ensembles as $ensemble)
<tr data-id="{{ $ensemble->id }}"> <tr data-id="{{ $ensemble->id }}" id="ensembleRow-{{$ensemble->id}}">
<x-table.td class="handle"> <x-table.td class="handle">
<svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"> <svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5 7h14M5 12h14M5 17h14"/> <path stroke="currentColor" stroke-linecap="round" stroke-width="2" d="M5 7h14M5 12h14M5 17h14"/>

View File

@ -1,7 +1,7 @@
<x-modal> <x-modal>
<x-slot:button_text>{{ $ensemble->name }}</x-slot:button_text> <x-slot:button_text>{{ $ensemble->name }}</x-slot:button_text>
<x-slot:title class="font-semibold">Rename Ensemble {{ $ensemble->name }}</x-slot:title> <x-slot:title class="font-semibold">Rename Ensemble {{ $ensemble->name }}</x-slot:title>
<x-form.form method="PATCH" action="{{ route('admin.ensembles.updateEnsemble', ['ensemble' => $ensemble->rank]) }}"> <x-form.form method="PATCH" action="{{ route('admin.ensembles.update', ['ensemble' => $ensemble->rank]) }}">
<x-form.body-grid columns="4"> <x-form.body-grid columns="4">
<x-form.field colspan="2" label_text="New Name" value="{{ $ensemble->name }}" name="name" /> <x-form.field colspan="2" label_text="New Name" value="{{ $ensemble->name }}" name="name" />
<x-form.field colspan="1" label_text="New Code" value="{{ $ensemble->code }}" name="code" /> <x-form.field colspan="1" label_text="New Code" value="{{ $ensemble->code }}" name="code" />

View File

@ -19,7 +19,7 @@ Route::middleware(['auth', 'verified', CheckIfAdmin::class])->prefix('admin/')->
Route::post('/', 'store')->name('admin.ensembles.store'); Route::post('/', 'store')->name('admin.ensembles.store');
Route::delete('/{ensemble}', 'destroy')->name('admin.ensembles.destroy'); Route::delete('/{ensemble}', 'destroy')->name('admin.ensembles.destroy');
Route::post('/updateEnsembleRank', 'updateEnsembleRank')->name('admin.ensembles.updateEnsembleRank'); Route::post('/updateEnsembleRank', 'updateEnsembleRank')->name('admin.ensembles.updateEnsembleRank');
Route::patch('/{ensemble}', 'updateEnsemble')->name('admin.ensembles.updateEnsemble'); Route::patch('/{ensemble}', 'updateEnsemble')->name('admin.ensembles.update');
Route::get('/seating-limits', 'seatingLimits')->name('admin.ensembles.seatingLimits'); Route::get('/seating-limits', 'seatingLimits')->name('admin.ensembles.seatingLimits');
Route::get('/seating-limits/{ensemble}', 'seatingLimits')->name('admin.ensembles.seatingLimits.ensemble'); Route::get('/seating-limits/{ensemble}', 'seatingLimits')->name('admin.ensembles.seatingLimits.ensemble');
Route::post('/seating-limits/{ensemble}', 'seatingLimitsSet')->name('admin.ensembles.seatingLimits.ensemble.set'); Route::post('/seating-limits/{ensemble}', 'seatingLimitsSet')->name('admin.ensembles.seatingLimits.ensemble.set');

View File

@ -0,0 +1,134 @@
<?php
use App\Models\Audition;
use App\Models\Ensemble;
use App\Models\Entry;
use App\Models\Event;
use App\Models\Seat;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\delete;
use function Pest\Laravel\get;
use function Pest\Laravel\patch;
uses(RefreshDatabase::class);
it('allows only an admin to manage ensembles', function () {
get((route('admin.ensembles.index')))
->assertRedirect(route('home'));
actAsNormal();
get((route('admin.ensembles.index')))
->assertRedirect('/dashboard')
->assertSessionHas('error', 'You are not authorized to perform this action');
actasAdmin();
get((route('admin.ensembles.index')))
->assertOk();
});
it('has a form to create an ensemble', function () {
// Arrange
actAsAdmin();
// Act & Assert
get((route('admin.ensembles.index')))
->assertOk()
->assertSee('action="'.route('admin.ensembles.store').'"', false)
->assertSee('name="name"', false)
->assertSee('name="code"', false)
->assertSee('name="event_id"', false)
->assertSeeInOrder(['<button', 'type="submit"', '</button>'], false);
});
it('shows ensemble data', function () {
// Arrange
Ensemble::factory()->count(10)->create();
actAsAdmin();
// Act & Assert
$response = get((route('admin.ensembles.index')));
$response->assertOk();
$events = Event::all();
foreach ($events as $event) {
foreach ($event->ensembles as $ensemble) {
$response->assertSeeInOrder([
$event->name, '<td', $ensemble->id, '</td>', '<td', e($ensemble->name), '</td>',
], false);
}
$response->assertSee($event->name);
}
});
it('shows a delete option for ensembles with no students seated', function () {
// Arrange
$event = Event::factory()->create();
$noSeatsEnsemble = Ensemble::factory()->create(['event_id' => $event->id]);
$seatsEnsemble = Ensemble::factory()->create(['event_id' => $event->id]);
$audition = Audition::factory()->create(['event_id' => $event->id]);
$entry = Entry::factory()->create(['audition_id' => $audition->id]);
Seat::create([
'ensemble_id' => $seatsEnsemble->id,
'audition_id' => $audition->id,
'seat' => 1,
'entry_id' => $entry->id,
]);
// Act & Assert
actAsAdmin();
$response = get((route('admin.ensembles.index')));
$response->assertOk();
$response->assertSee(route('admin.ensembles.destroy', $noSeatsEnsemble), false);
#$response->assertDontSee(route('admin.ensembles.destroy', $seatsEnsemble), false); // TODO figure out how to test for a delete form that does not also see an edit form
});
it('allows an administrator to delete an ensemble while no entries are seated', function () {
// Arrange
$event = Event::factory()->create();
$noSeatsEnsemble = Ensemble::factory()->create(['event_id' => $event->id]);
$seatsEnsemble = Ensemble::factory()->create(['event_id' => $event->id]);
$audition = Audition::factory()->create(['event_id' => $event->id]);
$entry = Entry::factory()->create(['audition_id' => $audition->id]);
Seat::create([
'ensemble_id' => $seatsEnsemble->id,
'audition_id' => $audition->id,
'seat' => 1,
'entry_id' => $entry->id,
]);
// Act & Assert
actAsAdmin();
delete((route('admin.ensembles.destroy', $noSeatsEnsemble)))
->assertRedirect(route('admin.ensembles.index'))
->assertSessionHas('success', 'Ensemble deleted successfully');
expect(Ensemble::find($noSeatsEnsemble->id))->toBeNull();
delete((route('admin.ensembles.destroy', $seatsEnsemble)))
->assertRedirect(route('admin.ensembles.index'))
->assertSessionHas('error', 'Ensemble has students seated and cannot be deleted');
});
it('does not allow a guest or normal user to delete an ensemble', function () {
$ensemble = Ensemble::factory()->create();
delete((route('admin.ensembles.destroy', $ensemble)))
->assertRedirect(route('home'));
actAsNormal();
delete((route('admin.ensembles.destroy', $ensemble)))
->assertRedirect('/dashboard')
->assertSessionHas('error', 'You are not authorized to perform this action');
});
it('includes a form to edit an ensemble', function () {
// Arrange
$ensemble = Ensemble::factory()->create();
actAsAdmin();
// Act & Assert
get((route('admin.ensembles.index')))
->assertOk()
->assertSee('action="'.route('admin.ensembles.update', $ensemble).'"', false);
});
it('allows an administrator to update an ensemble', function () {
// Arrange
$ensemble = Ensemble::factory()->create();
$newData = [
'name' => 'New Ensemble Name',
'code' => 'NEWC',
];
// Act & Assert
actAsAdmin();
$response = patch(route('admin.ensembles.update', $ensemble), $newData);
/** @noinspection PhpUnhandledExceptionInspection */
$response->assertRedirect(route('admin.ensembles.index'))
->assertSessionHas('success', 'Ensemble updated successfully')
->assertSessionHasNoErrors();
$postCheck = Ensemble::find($ensemble->id);
expect($postCheck->name)->toBe($newData['name'])
->and($postCheck->code)->toBe($newData['code']);
});