From a37ccd64df280c728a1442eaa39c44dfb353dd22 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Tue, 31 Mar 2026 10:56:54 +0700 Subject: [PATCH] feat: add CourseSchedule model factory and feature tests for CRUD operations and authorization --- database/factories/CourseScheduleFactory.php | 27 +++ tests/Feature/CourseScheduleTest.php | 196 +++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 database/factories/CourseScheduleFactory.php create mode 100644 tests/Feature/CourseScheduleTest.php diff --git a/database/factories/CourseScheduleFactory.php b/database/factories/CourseScheduleFactory.php new file mode 100644 index 0000000..8fa657b --- /dev/null +++ b/database/factories/CourseScheduleFactory.php @@ -0,0 +1,27 @@ + + */ +class CourseScheduleFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'course_id' => Course::factory(), + 'day_of_week' => fake()->numberBetween(1, 7), + 'start_time' => '08:00', + 'end_time' => '10:00', + ]; + } +} diff --git a/tests/Feature/CourseScheduleTest.php b/tests/Feature/CourseScheduleTest.php new file mode 100644 index 0000000..402c5a2 --- /dev/null +++ b/tests/Feature/CourseScheduleTest.php @@ -0,0 +1,196 @@ +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); + }); +});