feat: add CourseSchedule model factory and feature tests for CRUD operations and authorization

This commit is contained in:
Yoga Pangestu 2026-03-31 10:56:54 +07:00
parent 9d7a5d206c
commit a37ccd64df
2 changed files with 223 additions and 0 deletions

View File

@ -0,0 +1,27 @@
<?php
namespace Database\Factories;
use App\Models\Course;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\CourseSchedule>
*/
class CourseScheduleFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'course_id' => Course::factory(),
'day_of_week' => fake()->numberBetween(1, 7),
'start_time' => '08:00',
'end_time' => '10:00',
];
}
}

View File

@ -0,0 +1,196 @@
<?php
use App\Enums\RoleEnum;
use App\Filament\Pages\Information\CourseSchedule\Index as CourseSchedulePage;
use App\Models\Course;
use App\Models\CourseSchedule;
use App\Models\User;
use App\Settings\GeneralSettings;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
/**
* Setup necessary roles and permissions
*/
beforeEach(function () {
Role::findOrCreate(RoleEnum::Developer->value);
Role::findOrCreate(RoleEnum::Kosma->value);
Role::findOrCreate(RoleEnum::Student->value);
$permissions = [
'View:CourseSchedule',
'Create:CourseSchedule',
'Update:CourseSchedule',
'Delete:CourseSchedule',
];
foreach ($permissions as $permission) {
Permission::findOrCreate($permission);
}
});
describe('Course Schedule Authorization', function () {
it('allows all roles to view the page but restricts actions to Developer and Kosma', function () {
$developer = User::factory()->create();
$developer->assignRole(RoleEnum::Developer);
$developer->givePermissionTo(['View:CourseSchedule', 'Create:CourseSchedule']);
$kosma = User::factory()->create();
$kosma->assignRole(RoleEnum::Kosma);
$kosma->givePermissionTo(['View:CourseSchedule', 'Create:CourseSchedule']);
$regularStudent = User::factory()->create();
$regularStudent->assignRole(RoleEnum::Student);
$regularStudent->givePermissionTo('View:CourseSchedule');
// All can view the page
$this->actingAs($developer);
Livewire::test(CourseSchedulePage::class)->assertSuccessful();
$this->actingAs($kosma);
Livewire::test(CourseSchedulePage::class)->assertSuccessful();
$this->actingAs($regularStudent);
Livewire::test(CourseSchedulePage::class)->assertSuccessful();
// Only Developer and Kosma can see the Create action
$this->actingAs($developer);
Livewire::test(CourseSchedulePage::class)->assertActionVisible('createSchedule');
$this->actingAs($kosma);
Livewire::test(CourseSchedulePage::class)->assertActionVisible('createSchedule');
$this->actingAs($regularStudent);
Livewire::test(CourseSchedulePage::class)->assertActionHidden('createSchedule');
});
});
describe('Course Schedule Model', function () {
it('has a relationship with Course', function () {
$schedule = CourseSchedule::factory()->create();
expect($schedule->course)->toBeInstanceOf(Course::class);
});
it('supports Soft Deletes', function () {
$schedule = CourseSchedule::factory()->create();
$schedule->delete();
expect($schedule->trashed())->toBeTrue();
expect(CourseSchedule::find($schedule->id))->toBeNull();
});
});
describe('Course Schedule CRUD via Custom Actions', function () {
beforeEach(function () {
$user = User::factory()->create();
$user->assignRole(RoleEnum::Developer);
$user->givePermissionTo(['View:CourseSchedule', 'Create:CourseSchedule', 'Update:CourseSchedule', 'Delete:CourseSchedule']);
$this->actingAs($user);
// Ensure semester matches settings for visibility
$this->currentSemester = app(GeneralSettings::class)->current_semester;
});
it('can create a new schedule', function () {
$course = Course::factory()->create(['semester' => $this->currentSemester]);
Livewire::test(CourseSchedulePage::class)
->callAction('createSchedule', [
'course_id' => $course->id,
'day_of_week' => 1, // Senin
'start_time' => '10:00',
'end_time' => '12:00',
])
->assertHasNoActionErrors();
$this->assertDatabaseHas('course_schedules', [
'course_id' => $course->id,
'day_of_week' => 1,
'start_time' => '10:00',
'end_time' => '12:00',
]);
});
it('can update an existing schedule', function () {
$course = Course::factory()->create(['semester' => $this->currentSemester]);
$schedule = CourseSchedule::factory()->create([
'course_id' => $course->id,
'day_of_week' => 2, // Selasa
]);
Livewire::test(CourseSchedulePage::class)
->callAction('editSchedule', [
'day_of_week' => 3, // Rabu
], ['schedule' => $schedule->id])
->assertHasNoActionErrors();
expect($schedule->refresh()->day_of_week)->toBe(3);
});
it('can delete a schedule', function () {
$course = Course::factory()->create(['semester' => $this->currentSemester]);
$schedule = CourseSchedule::factory()->create(['course_id' => $course->id]);
Livewire::test(CourseSchedulePage::class)
->callAction('deleteSchedule', [], ['schedule' => $schedule->id])
->assertHasNoActionErrors();
expect(CourseSchedule::find($schedule->id))->toBeNull();
});
});
describe('Search and Search Filtering', function () {
beforeEach(function () {
$user = User::factory()->create();
$user->assignRole(RoleEnum::Developer);
$user->givePermissionTo(['View:CourseSchedule']);
$this->actingAs($user);
$this->currentSemester = app(GeneralSettings::class)->current_semester;
});
it('can search schedules by course name or lecturer', function () {
$targetCourse = Course::factory()->create([
'name' => 'Specific Course Name',
'lecturer' => 'Specific Lecturer',
'semester' => $this->currentSemester,
]);
$targetSchedule = CourseSchedule::factory()->create(['course_id' => $targetCourse->id]);
$otherCourse = Course::factory()->create([
'name' => 'Other Course Name',
'semester' => $this->currentSemester,
]);
$otherSchedule = CourseSchedule::factory()->create(['course_id' => $otherCourse->id]);
// Search by course name
Livewire::test(CourseSchedulePage::class)
->set('search', 'Specific Course Name')
->assertSet('search', 'Specific Course Name');
// Since it's a custom page, we check visibility within the computed property or the view
// The schedules are grouped by day name (translated)
$dayNames = [1 => 'Senin', 2 => 'Selasa', 3 => 'Rabu', 4 => 'Kamis', 5 => 'Jumat', 6 => 'Sabtu', 7 => 'Minggu'];
$dayName = $dayNames[$targetSchedule->day_of_week];
Livewire::test(CourseSchedulePage::class)
->set('search', 'Specific Course Name')
->assertSee($targetCourse->name)
->assertDontSee($otherCourse->name);
// Search by lecturer
Livewire::test(CourseSchedulePage::class)
->set('search', 'Specific Lecturer')
->assertSee($targetCourse->name)
->assertDontSee($otherCourse->name);
});
it('only shows schedules for the current semester', function () {
$otherSemester = $this->currentSemester + 1;
$otherCourse = Course::factory()->create(['name' => 'Other Semester Course', 'semester' => $otherSemester]);
$otherSchedule = CourseSchedule::factory()->create(['course_id' => $otherCourse->id]);
Livewire::test(CourseSchedulePage::class)
->assertDontSee($otherCourse->name);
});
});