Finish Audition model test and student index. Finish AuditionFlag model test

This commit is contained in:
Matt Young 2024-07-01 08:14:30 -05:00
parent 0de7edf6e9
commit f400b2bda5
19 changed files with 418 additions and 64 deletions

View File

@ -5,10 +5,10 @@ namespace App\Http\Controllers;
use App\Models\Audition; use App\Models\Audition;
use App\Models\School; use App\Models\School;
use App\Models\Student; use App\Models\Student;
use App\Models\User;
use App\Rules\UniqueFullNameAtSchool; use App\Rules\UniqueFullNameAtSchool;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use function abort; use function abort;
use function redirect; use function redirect;
@ -20,8 +20,12 @@ class StudentController extends Controller
*/ */
public function index() public function index()
{ {
if (! Auth::user()->school_id) {
return redirect()->route('dashboard');
}
$students = Auth::user()->students()->with('entries')->get(); $students = Auth::user()->students()->with('entries')->get();
$auditions = Audition::all(); $auditions = Audition::all();
return view('students.index', ['students' => $students, 'auditions' => $auditions]); return view('students.index', ['students' => $students, 'auditions' => $auditions]);
} }
@ -38,10 +42,15 @@ class StudentController extends Controller
*/ */
public function store(Request $request) public function store(Request $request)
{ {
if ($request->user()->cannot('create', Student::class)) abort(403); if ($request->user()->cannot('create', Student::class)) {
abort(403);
}
$request->validate([ $request->validate([
'first_name' => ['required'], 'first_name' => ['required'],
'last_name' => ['required', new UniqueFullNameAtSchool(request('first_name'),request('last_name'), Auth::user()->school_id)], 'last_name' => [
'required',
new UniqueFullNameAtSchool(request('first_name'), request('last_name'), Auth::user()->school_id),
],
'grade' => ['required', 'integer'], 'grade' => ['required', 'integer'],
]); ]);
@ -49,7 +58,7 @@ class StudentController extends Controller
'first_name' => request('first_name'), 'first_name' => request('first_name'),
'last_name' => request('last_name'), 'last_name' => request('last_name'),
'grade' => request('grade'), 'grade' => request('grade'),
'school_id' => Auth::user()->school_id 'school_id' => Auth::user()->school_id,
]); ]);
$request->session()->put('auditionMessages', ['success', 'I did it again ma']); $request->session()->put('auditionMessages', ['success', 'I did it again ma']);
@ -70,7 +79,10 @@ class StudentController extends Controller
*/ */
public function edit(Request $request, Student $student) public function edit(Request $request, Student $student)
{ {
if ($request->user()->cannot('update', $student)) abort(403); if ($request->user()->cannot('update', $student)) {
abort(403);
}
return view('students.edit', ['student' => $student]); return view('students.edit', ['student' => $student]);
} }
@ -79,7 +91,9 @@ class StudentController extends Controller
*/ */
public function update(Request $request, Student $student) public function update(Request $request, Student $student)
{ {
if ($request->user()->cannot('update', $student)) abort(403); if ($request->user()->cannot('update', $student)) {
abort(403);
}
request()->validate([ request()->validate([
'first_name' => ['required'], 'first_name' => ['required'],
'last_name' => ['required'], 'last_name' => ['required'],
@ -89,12 +103,11 @@ class StudentController extends Controller
$student->update([ $student->update([
'first_name' => request('first_name'), 'first_name' => request('first_name'),
'last_name' => request('last_name'), 'last_name' => request('last_name'),
'grade' => request('grade') 'grade' => request('grade'),
]); ]);
// TODO if a students grade is changed, we need to be sure they are still eligible for the auditions in which they are entered. // TODO if a students grade is changed, we need to be sure they are still eligible for the auditions in which they are entered.
return redirect('/students'); return redirect('/students');
} }
@ -103,8 +116,11 @@ class StudentController extends Controller
*/ */
public function destroy(Request $request, Student $student) public function destroy(Request $request, Student $student)
{ {
if ($request->user()->cannot('delete', $student)) abort(403); if ($request->user()->cannot('delete', $student)) {
abort(403);
}
$student->delete(); $student->delete();
return redirect('/students'); return redirect('/students');
} }
} }

View File

@ -20,7 +20,7 @@ class CheckIfAdmin
return $next($request); return $next($request);
} }
return redirect('/')->with('error', 'You do not have admin access.'); return redirect(route('home'))->with('error', 'You do not have admin access.');
} }
} }

View File

@ -4,7 +4,10 @@ namespace App\Http\Middleware;
use Closure; use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use function redirect;
use function route;
class CheckIfHasSchool class CheckIfHasSchool
{ {
@ -15,6 +18,11 @@ class CheckIfHasSchool
*/ */
public function handle(Request $request, Closure $next): Response public function handle(Request $request, Closure $next): Response
{ {
if (Auth::check() && Auth::user()->school_id) {
return $next($request); return $next($request);
} }
return redirect(route('dashboard'))->with('error', 'You do not have a school to view students for.');
}
} }

View File

@ -10,8 +10,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use function now;
class Audition extends Model class Audition extends Model
{ {
use HasFactory; use HasFactory;
@ -26,7 +24,6 @@ class Audition extends Model
protected $scored_entries_count; //Set by TabulationService protected $scored_entries_count; //Set by TabulationService
public function event(): BelongsTo public function event(): BelongsTo
{ {
return $this->belongsTo(Event::class); return $this->belongsTo(Event::class);
@ -175,12 +172,14 @@ class Audition extends Model
} }
$this->flags()->create(['flag_name' => $flag]); $this->flags()->create(['flag_name' => $flag]);
$this->load('flags');
} }
public function removeFlag($flag): void public function removeFlag($flag): void
{ {
// remove related auditionFlag where flag_name = $flag // remove related auditionFlag where flag_name = $flag
$this->flags()->where('flag_name', $flag)->delete(); $this->flags()->where('flag_name', $flag)->delete();
$this->load('flags');
} }
public function scopeOpen(Builder $query): void public function scopeOpen(Builder $query): void

View File

@ -5,7 +5,7 @@ namespace App\Policies;
use App\Models\Entry; use App\Models\Entry;
use App\Models\Student; use App\Models\Student;
use App\Models\User; use App\Models\User;
use Illuminate\Auth\Access\Response;
use function is_null; use function is_null;
class StudentPolicy class StudentPolicy
@ -31,7 +31,10 @@ class StudentPolicy
*/ */
public function create(User $user): bool public function create(User $user): bool
{ {
if($user->is_admin) return true; if ($user->is_admin) {
return true;
}
return ! is_null($user->school_id); return ! is_null($user->school_id);
} }
@ -41,7 +44,10 @@ class StudentPolicy
public function update(User $user, Student $student): bool public function update(User $user, Student $student): bool
{ {
if($user->is_admin) return true; if ($user->is_admin) {
return true;
}
return $user->school_id == $student->school_id; return $user->school_id == $student->school_id;
} }
@ -50,7 +56,10 @@ class StudentPolicy
*/ */
public function delete(User $user, Student $student): bool public function delete(User $user, Student $student): bool
{ {
if (Entry::where('student_id','=',$student->id)->exists()) return false; // Don't allow deletion of a student with entries if (Entry::where('student_id', '=', $student->id)->exists()) {
return false;
} // Don't allow deletion of a student with entries
return $user->school_id == $student->school_id; return $user->school_id == $student->school_id;
} }

View File

@ -19,7 +19,7 @@ class UniqueFullNameAtSchool implements ValidationRule
$this->school_id = $schoolID; $this->school_id = $schoolID;
} }
public function passes($attributies, $value) public function studentExists()
{ {
return Student::where('first_name', $this->first_name) return Student::where('first_name', $this->first_name)
->where('last_name', $this->last_name) ->where('last_name', $this->last_name)
@ -38,6 +38,8 @@ class UniqueFullNameAtSchool implements ValidationRule
*/ */
public function validate(string $attribute, mixed $value, Closure $fail): void public function validate(string $attribute, mixed $value, Closure $fail): void
{ {
// if($this->studentExists()) {
$fail($this->message());
}
} }
} }

View File

@ -41,7 +41,7 @@ class AuditionFactory extends Factory
return [ return [
'event_id' => $event->id, 'event_id' => $event->id,
'name' => $this->faker->randomElement($instruments).$this->faker->randomNumber(1), 'name' => $this->faker->randomElement($instruments).$this->faker->randomNumber(3),
'score_order' => 1, 'score_order' => 1,
'entry_deadline' => Carbon::tomorrow(), 'entry_deadline' => Carbon::tomorrow(),
'entry_fee' => 1000, 'entry_fee' => 1000,

View File

@ -18,7 +18,7 @@ class RoomFactory extends Factory
{ {
return [ return [
'name' => 'Room '.fake()->numberBetween(7, 500), 'name' => 'Room '.fake()->numberBetween(7, 500),
'description' => fake()->sentence() 'description' => fake()->sentence(),
]; ];
} }
} }

View File

@ -17,7 +17,7 @@ class ScoringGuideFactory extends Factory
public function definition(): array public function definition(): array
{ {
return [ return [
// 'name' => $this->faker->sentence(3),
]; ];
} }
} }

View File

@ -6,7 +6,7 @@ use App\Models\SiteSetting;
use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder; use Illuminate\Database\Seeder;
class SampleSettings extends Seeder class SampleSettingsSeeder extends Seeder
{ {
/** /**
* Run the database seeds. * Run the database seeds.
@ -55,10 +55,6 @@ class SampleSettings extends Seeder
]); ]);
SiteSetting::create([ SiteSetting::create([
'setting_key' => 'school_fee', 'setting_key' => 'school_fee',
'setting_value' => 'SBDA',
]);
SiteSetting::create([
'setting_key' => 'auditionAbbreviation',
'setting_value' => '2500', 'setting_value' => '2500',
]); ]);
SiteSetting::create([ SiteSetting::create([
@ -66,12 +62,16 @@ class SampleSettings extends Seeder
'setting_value' => '143 Sousa Lane', 'setting_value' => '143 Sousa Lane',
]); ]);
SiteSetting::create([ SiteSetting::create([
'setting_key' => 'auditionAbbreviation', 'setting_key' => 'payment_city',
'setting_value' => 'SBDA', 'setting_value' => 'Maud',
]); ]);
SiteSetting::create([ SiteSetting::create([
'setting_key' => 'auditionAbbreviation', 'setting_key' => 'payment_state',
'setting_value' => 'SBDA', 'setting_value' => 'OK',
]);
SiteSetting::create([
'setting_key' => 'payment_zip',
'setting_value' => '77777',
]); ]);
} }
} }

View File

@ -2,7 +2,7 @@
{{-- <button type="button" class="inline-flex items-center gap-x-1 text-sm font-semibold leading-6 text-gray-900" aria-expanded="false" @on:click=" open = ! open">--}} {{-- <button type="button" class="inline-flex items-center gap-x-1 text-sm font-semibold leading-6 text-gray-900" aria-expanded="false" @on:click=" open = ! open">--}}
<button type="button" class="inline-flex items-center gap-x-1 text-white rounded-md px-3 py-2 text-sm font-medium hover:bg-indigo-500 hover:bg-opacity-75" aria-expanded="false" @click=" open = ! open" @click.outside=" open = false"> <button type="button" class="inline-flex items-center gap-x-1 text-white rounded-md px-3 py-2 text-sm font-medium hover:bg-indigo-500 hover:bg-opacity-75" aria-expanded="false" @click=" open = ! open" @click.outside=" open = false">
<span>Admin</span> <span>Administration</span>
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" /> <path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" />
</svg> </svg>

View File

@ -14,16 +14,16 @@
</div> </div>
<div class="hidden md:block"> <div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4"> <div class="ml-10 flex items-baseline space-x-4">
<x-layout.navbar.nav-link href="/dashboard" :active="request()->is('dashboard')">Dashboard <x-layout.navbar.nav-link href="{{ route('dashboard') }}" :active="request()->is('dashboard')">Dashboard
</x-layout.navbar.nav-link> </x-layout.navbar.nav-link>
@if(Auth::user()->school_id) @if(Auth::user()->school_id)
<x-layout.navbar.nav-link href="/students" :active="request()->is('students')">Students <x-layout.navbar.nav-link href="{{ route('students.index') }}" :active="request()->is('students')">Students
</x-layout.navbar.nav-link> </x-layout.navbar.nav-link>
<x-layout.navbar.nav-link href="/entries" :active="request()->is('entries')">Entries <x-layout.navbar.nav-link href="{{ route('entries.index') }}" :active="request()->is('entries')">Entries
</x-layout.navbar.nav-link> </x-layout.navbar.nav-link>
@endif @endif
@if(Auth::user()->isJudge() AND Settings::get('judging_enabled')) @if(Auth::user()->isJudge() AND Settings::get('judging_enabled'))
<x-layout.navbar.nav-link href="/judging" :active="request()->is('judging')">Judging <x-layout.navbar.nav-link href="{{ route('judging.index') }}" :active="request()->is('judging')">Judging
</x-layout.navbar.nav-link> </x-layout.navbar.nav-link>
@endif @endif
@if(Auth::user()->is_admin) @if(Auth::user()->is_admin)
@ -164,8 +164,7 @@
<!-- Current: "bg-indigo-700 text-white", Default: "text-white hover:bg-indigo-500 hover:bg-opacity-75" --> <!-- Current: "bg-indigo-700 text-white", Default: "text-white hover:bg-indigo-500 hover:bg-opacity-75" -->
<a href="/dashboard" class="bg-indigo-700 text-white block rounded-md px-3 py-2 text-base font-medium" <a href="/dashboard" class="bg-indigo-700 text-white block rounded-md px-3 py-2 text-base font-medium"
aria-current="page">Dashboard</a> aria-current="page">Dashboard</a>
<a href="/students"
class="text-white hover:bg-indigo-500 hover:bg-opacity-75 block rounded-md px-3 py-2 text-base font-medium">Students</a>
</div> </div>
<div class="border-t border-indigo-700 pb-3 pt-4"> <div class="border-t border-indigo-700 pb-3 pt-4">
<div class="flex items-center px-5"> <div class="flex items-center px-5">

View File

@ -2,7 +2,7 @@
<x-layout.app> <x-layout.app>
<x-slot:page_title>Dashboard</x-slot:page_title> <x-slot:page_title>Dashboard</x-slot:page_title>
@if(! Auth::user()->school_id) @if(! Auth::user()->school_id)
You aren't currently associated with a school. <a href="/my_school" class="text-blue-600">Click here to choose or create one.</a> <p class="pb-5">You aren't currently associated with a school. <a href="/my_school" class="text-blue-600">Click here to choose or create one.</a></p>
@endif @endif
<div class="grid sm:grid-cols-2 md:grid-cols-4"> <div class="grid sm:grid-cols-2 md:grid-cols-4">
<div>{{-- Column 1 --}} <div>{{-- Column 1 --}}

View File

@ -19,32 +19,32 @@ Route::middleware(['auth', 'verified'])->group(function () {
// Entry Related Routes // Entry Related Routes
Route::middleware(['auth', 'verified', 'can:create,App\Models\Entry'])->controller(EntryController::class)->group(function () { Route::middleware(['auth', 'verified', 'can:create,App\Models\Entry'])->controller(EntryController::class)->group(function () {
Route::get('/entries', 'index'); Route::get('/entries', 'index')->name('entries.index');
Route::get('/entries/create', 'create'); Route::get('/entries/create', 'create')->name('entries.create');
Route::post('/entries', 'store'); Route::post('/entries', 'store')->name('entries.store');
Route::delete('/entries/{entry}', 'destroy'); Route::delete('/entries/{entry}', 'destroy')->name('entries.destroy');
}); });
// User Related Routes // User Related Routes
Route::middleware(['auth', 'verified'])->controller(UserController::class)->group(function () { Route::middleware(['auth', 'verified'])->controller(UserController::class)->group(function () {
Route::patch('/users/{user}/set_school', 'set_school'); Route::patch('/users/{user}/set_school', 'set_school')->name('users.set_school');
Route::patch('/users/{$user}', 'update'); Route::patch('/users/{$user}', 'update')->name('users.update');
}); });
// Student Related Routes // Student Related Routes
Route::middleware(['auth', 'verified', 'can:create,App\Models\Student'])->controller(StudentController::class)->group(function () { Route::middleware(['auth', 'verified', 'can:create,App\Models\Student'])->controller(StudentController::class)->group(function () {
Route::get('/students', 'index'); Route::get('/students', 'index')->name('students.index');
Route::post('students', 'store'); Route::post('students', 'store')->name('students.store');
Route::get('/students/{student}/edit', 'edit'); Route::get('/students/{student}/edit', 'edit')->name('students.edit');
Route::patch('/students/{student}', 'update'); Route::patch('/students/{student}', 'update')->name('students.update');
Route::delete('/students/{student}', 'destroy'); Route::delete('/students/{student}', 'destroy')->name('students.destroy');
}); });
// School Related Routes // School Related Routes
Route::middleware(['auth', 'verified'])->controller(SchoolController::class)->group(function () { Route::middleware(['auth', 'verified'])->controller(SchoolController::class)->group(function () {
Route::get('/schools/create', 'create'); Route::get('/schools/create', 'create')->name('schools.create');
Route::post('/schools', 'store'); Route::post('/schools', 'store')->name('schools.store');
Route::get('/schools/{school}/edit', 'edit'); Route::get('/schools/{school}/edit', 'edit')->name('schools.edit');
Route::get('/schools/{school}', 'show')->name('schools.show'); Route::get('/schools/{school}', 'show')->name('schools.show')->name('schools.show');
Route::patch('/schools/{school}', 'update'); Route::patch('/schools/{school}', 'update')->name('schools.update');
}); });

View File

@ -0,0 +1,21 @@
<?php
use App\Models\Audition;
use App\Models\AuditionFlag;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('has an audition', function () {
$audition = Audition::factory()->create();
// Arrange
$flag = AuditionFlag::create([
'audition_id' => $audition->id,
'flag_name' => 'Test Flag',
]);
// Act and Assert
expect($flag->audition->name)->toBe($audition->name);
});

View File

@ -1,6 +1,12 @@
<?php <?php
use App\Models\Audition; use App\Models\Audition;
use App\Models\AuditionFlag;
use App\Models\Entry;
use App\Models\Event;
use App\Models\Room;
use App\Models\ScoringGuide;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class); uses(RefreshDatabase::class);
@ -63,3 +69,102 @@ it('only returns published advancement auditions for advancementPublished scope'
->first()->id->toEqual($published->id); ->first()->id->toEqual($published->id);
}); });
it('has an event', function () {
// Arrange
$event = Event::factory()->create(['name' => 'Symphonic Concert Wind Ensemble Band']);
$audition = Audition::factory()->create(['event_id' => $event->id]);
// Act & Assert
expect($audition->event->name)->toEqual('Symphonic Concert Wind Ensemble Band');
});
it('has entries', function () {
// Arrange
$audition = Audition::factory()->create();
Entry::factory()->count(10)->create(['audition_id' => $audition->id]);
// Act & Assert
expect($audition->entries->count())->toEqual(10);
});
it('has a room', function () {
// Arrange
$room = Room::factory()->create(['name' => 'Room 1']);
$audition = Audition::factory()->create(['room_id' => $room->id]);
// Act & Assert
expect($audition->room->name)->toEqual('Room 1');
});
it('has a scoring guide', function () {
// Arrange
$sg = ScoringGuide::factory()->create(['name' => 'Sight Reading']);
$audition = Audition::factory()->create(['scoring_guide_id' => $sg->id]);
// Act & Assert
expect($audition->scoringGuide->name)->toEqual('Sight Reading');
});
it('displays the entry fee', function () {
// Arrange
$audition = Audition::factory()->create(['entry_fee' => 1000]);
// Act & Assert
expect($audition->display_fee())->toEqual('$10.00');
});
it('has many judges', function () {
// Arrange
$room = Room::factory()->create();
$audition = Audition::factory()->create(['room_id' => $room->id]);
$judges = User::factory()->count(5)->create();
foreach ($judges as $judge) {
$room->addJudge($judge->id);
}
// Act & Assert
expect($audition->judges->count())->toEqual(5);
});
it('has a judges_count available', function () {
// Arrange
$room = Room::factory()->create();
$audition = Audition::factory()->create(['room_id' => $room->id]);
$judges = User::factory()->count(5)->create();
foreach ($judges as $judge) {
$room->addJudge($judge->id);
}
// Act & Assert
expect($audition->judges_count)->toEqual(5);
});
it('can have flags', function () {
// Arrange
$audition = Audition::factory()->create();
AuditionFlag::create(['audition_id' => $audition->id, 'flag_name' => 'seats_published']);
AuditionFlag::create(['audition_id' => $audition->id, 'flag_name' => 'advance_published']);
// Act
// Assert
expect($audition->hasFlag('seats_published'))->toBeTrue();
expect($audition->hasFlag('notaflag'))->toBeFalse();
expect($audition->flags->count())->toEqual(2);
});
it('can add flags', function () {
// Arrange
$audition = Audition::factory()->create();
// Act
$audition->addFlag('seats_published');
// Assert
expect($audition->hasFlag('seats_published'))->toBeTrue();
});
it('can remove flags', function () {
// Arrange
$audition = Audition::factory()->create();
AuditionFlag::create(['audition_id' => $audition->id, 'flag_name' => 'seats_published']);
// Act & Assert
$audition->addFlag('seats_published');
expect($audition->hasFlag('seats_published'))->toBeTrue();
$audition->removeFlag('seats_published');
expect($audition->hasFlag('seats_published'))->toBeFalse();
});

View File

@ -1,5 +1,56 @@
<?php <?php
it('', function () { use App\Models\School;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\get;
uses(RefreshDatabase::class);
it('only shows Students and Entries menu options if the user has a school', function () {
// Act & Assert
$user = User::factory()->create();
$this->actingAs($user);
get(route('dashboard'))
->assertStatus(200)
->assertSeeText('Dashboard')
->assertDontSeeText('Students')
->assertDontSeeText('Entries');
$school = School::factory()->create();
$user->school_id = $school->id;
$user->save();
get(route('dashboard'))
->assertStatus(200)
->assertSeeText('My School')
->assertSeeText('Dashboard')
->assertSeeText('Students')
->assertSeeText('Entries');
});
it('only shows Admin menu if an administrator only shows Tabulation if admin or tabulator', function () {
// Arrange
$user = User::factory()->create();
$adminUser = User::factory()->admin()->create();
$tabUser = User::factory()->tab()->create();
// Act & Assert
$this->actingAs($user);
get(route('dashboard'))
->assertStatus(200)
->assertDontSeeText('Administration');
$this->actingAs($adminUser);
get(route('dashboard'))
->assertStatus(200)
->assertSeeText('Administration')
->assertSeeText('Tabulation');
$this->actingAs($tabUser);
get(route('dashboard'))
->assertStatus(200)
->assertDontSeeText('Administration')
->assertSeeText('Tabulation');
}); });

View File

@ -1,5 +1,134 @@
<?php <?php
it('', function () { use App\Models\School;
use App\Models\Student;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\get;
use function Pest\Laravel\post;
uses(RefreshDatabase::class);
it('cannot be accessed by a guest', function () {
// Act & Assert
get(route('students.index'))
->assertStatus(302)
->assertRedirect(route('home'));
});
it('cannot be accessed by a user without a school', function () {
// Arrange
$user = User::factory()->create();
$userWithSchool = User::factory()->create([
'school_id' => School::factory()->create()->id,
]);
// Act & Assert
$this->actingAs($user);
get('/students')
->assertStatus(403);
$this->actingAs($userWithSchool);
get('/students')
->assertStatus(200);
});
it('Shows a student for the user', function () {
// Arrange
$school = School::factory()->create();
$user = User::factory()->create([
'school_id' => $school->id,
]);
$student = Student::factory()->create([
'school_id' => $school->id,
]);
$students = [
['first_name' => 'John', 'last_name' => 'Lennon'],
['first_name' => 'Paul', 'last_name' => 'McCartney'],
['first_name' => 'George', 'last_name' => 'Harrison'],
['first_name' => 'Ringo', 'last_name' => 'Starr'],
];
foreach ($students as $s) {
Student::factory()->create([
'school_id' => $school->id,
'first_name' => $s['first_name'],
'last_name' => $s['last_name'],
]);
}
// Act and Assert
$this->actingAs($user);
get(route('students.index'))
->assertStatus(200)
->assertSeeText($student->name)
->assertSeeText(['Lennon, John', 'McCartney, Paul', 'Harrison, George', 'Starr, Ringo']);
});
it('does not show a student from another school', function () {
// Arrange
$school = School::factory()->create();
$user = User::factory()->create([
'school_id' => $school->id,
]);
$student = Student::factory()->create([
'school_id' => $school->id,
]);
$otherSchool = School::factory()->create();
$otherStudent = Student::factory()->create([
'school_id' => $otherSchool->id,
]);
// Act & Assert
$this->actingAs($user);
get(route('students.index'))
->assertStatus(200)
->assertSeeText($student->name)
->assertDontSeeText($otherStudent->name);
// Assert
});
it('can accept a new student entry', function () {
// Act and Assert
$school = School::factory()->create();
$user = User::factory()->create([
'school_id' => $school->id,
]);
$this->actingAs($user);
post(route('students.store'), [
'first_name' => 'James',
'last_name' => 'Brown',
'grade' => 10,
])
->assertSessionHasNoErrors()
->assertRedirect(route('students.index'));
get(route('students.index'))
->assertSeeText('Brown, James');
expect(Student::count())->toBe(1);
});
it('cannot have two students with identical names at a school', function () {
$school = School::factory()->create();
$user = User::factory()->create([
'school_id' => $school->id,
]);
Student::factory()->create([
'school_id' => $school->id,
'first_name' => 'James',
'last_name' => 'Brown',
]);
$this->actingAs($user);
post(route('students.store'), [
'first_name' => 'James',
'last_name' => 'Brown',
'grade' => 10,
])
->assertSessionHasErrors('last_name');
}); });

View File

@ -1,5 +1,6 @@
<?php <?php
use App\Models\School;
use App\Models\User; use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\RefreshDatabase;
@ -57,3 +58,17 @@ it('shows dashboard only if logged in', function () {
->assertSeeText('My School') ->assertSeeText('My School')
->assertSeeText('Dashboard'); ->assertSeeText('Dashboard');
}); });
it('shows students index only for a user with a school', function () {
// Act & Assert
$user = User::factory()->create();
$this->actingAs($user);
get(route('students.index'))
->assertStatus(403);
$school = School::factory()->create();
$user->school_id = $school->id;
get(route('students.index'))
->assertStatus(200)
->assertSeeText('Student Listing');
});