admin = User::factory()->admin()->create(); }); // =========================================== // Access Tests // =========================================== test('admin can access monthly report page', function () { $this->actingAs($this->admin) ->get(route('admin.reports.monthly')) ->assertOk() ->assertSee(__('report.monthly_report')); }); test('non-admin cannot access monthly report page', function () { $client = User::factory()->individual()->create(); $this->actingAs($client) ->get(route('admin.reports.monthly')) ->assertForbidden(); }); test('unauthenticated user cannot access monthly report page', function () { $this->get(route('admin.reports.monthly')) ->assertRedirect(route('login')); }); // =========================================== // Component Tests // =========================================== test('monthly report component mounts with previous month as default', function () { $this->actingAs($this->admin); $previousMonth = now()->subMonth(); Volt::test('admin.reports.monthly-report') ->assertSet('selectedPeriod', $previousMonth->format('Y-m')); }); test('available months shows only last 12 months', function () { $this->actingAs($this->admin); $component = Volt::test('admin.reports.monthly-report'); $availableMonths = $component->get('availableMonths'); expect(count($availableMonths))->toBe(12); }); test('available months does not include current month', function () { $this->actingAs($this->admin); $component = Volt::test('admin.reports.monthly-report'); $availableMonths = $component->get('availableMonths'); $currentMonth = now()->format('Y-m'); $values = array_column($availableMonths, 'value'); expect($values)->not->toContain($currentMonth); }); test('available months includes previous month', function () { $this->actingAs($this->admin); $component = Volt::test('admin.reports.monthly-report'); $availableMonths = $component->get('availableMonths'); $previousMonth = now()->subMonth()->format('Y-m'); $values = array_column($availableMonths, 'value'); expect($values)->toContain($previousMonth); }); // =========================================== // PDF Generation Tests // =========================================== test('monthly report generates valid PDF', function () { $this->actingAs($this->admin); // Create test data for the month $targetMonth = now()->subMonth(); User::factory()->count(5)->individual()->create([ 'created_at' => $targetMonth, ]); Consultation::factory()->count(10)->create([ 'booking_date' => $targetMonth, ]); Volt::test('admin.reports.monthly-report') ->set('selectedPeriod', $targetMonth->format('Y-m')) ->call('generate') ->assertFileDownloaded("monthly-report-{$targetMonth->year}-{$targetMonth->month}.pdf"); }); test('report handles month with no data gracefully', function () { $this->actingAs($this->admin); // Generate for a month with no data (far in the past) $emptyMonth = now()->subMonths(10); Volt::test('admin.reports.monthly-report') ->set('selectedPeriod', $emptyMonth->format('Y-m')) ->call('generate') ->assertFileDownloaded(); }); // =========================================== // Service Tests - User Statistics // =========================================== test('user statistics are accurate for selected month', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); // Create 3 users in target month User::factory()->count(3)->individual()->create([ 'created_at' => $targetMonth, ]); // Create 2 users in different month (should not be counted as new) User::factory()->count(2)->individual()->create([ 'created_at' => now()->subMonths(3), ]); $service = new MonthlyReportService; $stats = $service->getUserStats($startDate, $endDate); expect($stats['new_clients'])->toBe(3); }); test('user statistics count individual and company clients separately', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); User::factory()->count(4)->individual()->create([ 'created_at' => $targetMonth->copy()->subMonth(), ]); User::factory()->count(2)->company()->create([ 'created_at' => $targetMonth->copy()->subMonth(), ]); $service = new MonthlyReportService; $stats = $service->getUserStats($startDate, $endDate); expect($stats['individual'])->toBe(4); expect($stats['company'])->toBe(2); expect($stats['total_active'])->toBe(6); }); // =========================================== // Service Tests - Consultation Statistics // =========================================== test('consultation statistics calculate totals correctly', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); Consultation::factory()->count(5)->completed()->create([ 'booking_date' => $targetMonth, ]); Consultation::factory()->count(3)->pending()->create([ 'booking_date' => $targetMonth, ]); Consultation::factory()->count(2)->cancelled()->create([ 'booking_date' => $targetMonth, ]); $service = new MonthlyReportService; $stats = $service->getConsultationStats($startDate, $endDate); expect($stats['total'])->toBe(10); expect($stats['completed'])->toBe(5); expect($stats['cancelled'])->toBe(2); }); test('consultation statistics calculate no-show rate correctly', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); // 8 completed + 2 no-shows = 20% no-show rate Consultation::factory()->count(8)->completed()->create([ 'booking_date' => $targetMonth, ]); Consultation::factory()->count(2)->noShow()->create([ 'booking_date' => $targetMonth, ]); $service = new MonthlyReportService; $stats = $service->getConsultationStats($startDate, $endDate); expect($stats['no_show_rate'])->toBe(20.0); }); test('consultation statistics handle zero completed consultations', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); // Only pending consultations, no completed ones Consultation::factory()->count(5)->pending()->create([ 'booking_date' => $targetMonth, ]); $service = new MonthlyReportService; $stats = $service->getConsultationStats($startDate, $endDate); expect($stats['no_show_rate'])->toBe(0); }); test('consultation statistics count free and paid types', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); Consultation::factory()->count(4)->free()->create([ 'booking_date' => $targetMonth, ]); Consultation::factory()->count(6)->paid()->create([ 'booking_date' => $targetMonth, ]); $service = new MonthlyReportService; $stats = $service->getConsultationStats($startDate, $endDate); expect($stats['free'])->toBe(4); expect($stats['paid'])->toBe(6); }); // =========================================== // Service Tests - Timeline Statistics // =========================================== test('timeline statistics count active timelines correctly', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); Timeline::factory()->count(5)->active()->create([ 'created_at' => $targetMonth->copy()->subMonth(), ]); Timeline::factory()->count(2)->archived()->create([ 'created_at' => $targetMonth->copy()->subMonth(), ]); $service = new MonthlyReportService; $stats = $service->getTimelineStats($startDate, $endDate); expect($stats['active'])->toBe(5); }); test('timeline statistics count new timelines in month', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); Timeline::factory()->count(3)->create([ 'created_at' => $targetMonth, ]); Timeline::factory()->count(2)->create([ 'created_at' => now()->subMonths(3), ]); $service = new MonthlyReportService; $stats = $service->getTimelineStats($startDate, $endDate); expect($stats['new'])->toBe(3); }); test('timeline statistics count updates in month', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); $timeline = Timeline::factory()->create(); TimelineUpdate::factory()->count(5)->create([ 'timeline_id' => $timeline->id, 'created_at' => $targetMonth, ]); TimelineUpdate::factory()->count(2)->create([ 'timeline_id' => $timeline->id, 'created_at' => now()->subMonths(3), ]); $service = new MonthlyReportService; $stats = $service->getTimelineStats($startDate, $endDate); expect($stats['updates'])->toBe(5); }); // =========================================== // Service Tests - Post Statistics // =========================================== test('post statistics count published posts in month', function () { $targetMonth = now()->subMonth(); $startDate = $targetMonth->copy()->startOfMonth(); $endDate = $targetMonth->copy()->endOfMonth(); Post::factory()->count(4)->published()->create([ 'published_at' => $targetMonth, ]); Post::factory()->count(2)->published()->create([ 'published_at' => now()->subMonths(3), ]); $service = new MonthlyReportService; $stats = $service->getPostStats($startDate, $endDate); expect($stats['this_month'])->toBe(4); expect($stats['total'])->toBe(6); }); // =========================================== // Service Tests - Previous Month Comparison // =========================================== test('previous month comparison returns null when no prior data exists', function () { $targetMonth = now()->subMonth()->startOfMonth(); $service = new MonthlyReportService; $comparison = $service->getPreviousMonthComparison($targetMonth); expect($comparison)->toBeNull(); }); test('previous month comparison returns data when prior month has data', function () { $targetMonth = now()->subMonth(); $previousMonth = now()->subMonths(2); // Create data in the previous month User::factory()->count(3)->individual()->create([ 'created_at' => $previousMonth, ]); Consultation::factory()->count(5)->create([ 'booking_date' => $previousMonth, ]); $service = new MonthlyReportService; $comparison = $service->getPreviousMonthComparison($targetMonth->startOfMonth()); expect($comparison)->not->toBeNull(); expect($comparison['consultations'])->toBe(5); expect($comparison['clients'])->toBe(3); }); // =========================================== // Language Tests // =========================================== test('report respects admin language preference for Arabic', function () { $adminArabic = User::factory()->admin()->create(['preferred_language' => 'ar']); $this->actingAs($adminArabic); Volt::test('admin.reports.monthly-report') ->assertSee(__('report.monthly_report', [], 'ar')); }); test('report respects admin language preference for English', function () { $adminEnglish = User::factory()->admin()->create(['preferred_language' => 'en']); app()->setLocale('en'); $this->actingAs($adminEnglish); Volt::test('admin.reports.monthly-report') ->assertSee('Monthly Statistics Report'); }); // =========================================== // UI Elements Tests // =========================================== test('monthly report page shows table of contents preview', function () { $this->actingAs($this->admin); Volt::test('admin.reports.monthly-report') ->assertSee(__('report.table_of_contents')) ->assertSee(__('report.executive_summary')) ->assertSee(__('report.user_statistics')) ->assertSee(__('report.consultation_statistics')) ->assertSee(__('report.timeline_statistics')) ->assertSee(__('report.post_statistics')) ->assertSee(__('report.trends_chart')); }); test('monthly report page shows generate button', function () { $this->actingAs($this->admin); Volt::test('admin.reports.monthly-report') ->assertSee(__('report.generate')); }); test('monthly report page shows period selector', function () { $this->actingAs($this->admin); Volt::test('admin.reports.monthly-report') ->assertSee(__('report.select_period')); });