admin = User::factory()->admin()->create(); Cache::flush(); }); // =========================================== // Access Control Tests // =========================================== test('admin can view dashboard', function () { $this->actingAs($this->admin) ->get(route('admin.dashboard')) ->assertSuccessful() ->assertSee(__('admin_metrics.title')); }); test('non-admin cannot access dashboard', function () { $client = User::factory()->individual()->create(); $this->actingAs($client) ->get(route('admin.dashboard')) ->assertForbidden(); }); test('guest cannot access dashboard', function () { $this->get(route('admin.dashboard')) ->assertRedirect(route('login')); }); // =========================================== // User Metrics Tests // =========================================== test('dashboard displays correct user metrics', function () { User::factory()->count(5)->individual()->create(['status' => UserStatus::Active]); User::factory()->count(3)->company()->create(['status' => UserStatus::Active]); User::factory()->count(2)->individual()->create(['status' => UserStatus::Deactivated]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $userMetrics = $component->viewData('userMetrics'); expect($userMetrics['total_active'])->toBe(8) ->and($userMetrics['individual'])->toBe(5) ->and($userMetrics['company'])->toBe(3) ->and($userMetrics['deactivated'])->toBe(2); }); test('dashboard counts new clients this month correctly', function () { // Create clients this month User::factory()->count(3)->individual()->create([ 'status' => UserStatus::Active, 'created_at' => now(), ]); // Create clients from last month User::factory()->count(2)->individual()->create([ 'status' => UserStatus::Active, 'created_at' => now()->subMonth(), ]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $userMetrics = $component->viewData('userMetrics'); expect($userMetrics['new_this_month'])->toBe(3); }); test('user metrics excludes admin users from counts', function () { User::factory()->admin()->create(); User::factory()->count(2)->individual()->create(['status' => UserStatus::Active]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $userMetrics = $component->viewData('userMetrics'); // Should only count the 2 individual clients, not the admin expect($userMetrics['total_active'])->toBe(2); }); // =========================================== // Booking Metrics Tests // =========================================== test('dashboard displays correct booking metrics', function () { // Create 3 pending consultations Consultation::factory()->count(3)->create(['status' => ConsultationStatus::Pending]); // Create 2 approved consultations for today Consultation::factory()->count(2)->create([ 'status' => ConsultationStatus::Approved, 'booking_date' => today(), ]); // Create 5 free consultations (completed status to avoid counting as pending) Consultation::factory()->count(5)->create([ 'consultation_type' => ConsultationType::Free, 'status' => ConsultationStatus::Completed, ]); // Create 4 paid consultations (completed status to avoid counting as pending) Consultation::factory()->count(4)->create([ 'consultation_type' => ConsultationType::Paid, 'status' => ConsultationStatus::Completed, ]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $bookingMetrics = $component->viewData('bookingMetrics'); // 3 pending + 2 approved today (not pending) = 3 pending // 2 approved for today // 5 free + some from the other consultations // 4 paid + some from the other consultations expect($bookingMetrics['pending'])->toBe(3) ->and($bookingMetrics['today'])->toBe(2); }); test('no-show rate calculates correctly', function () { // Create 10 completed consultations Consultation::factory()->count(8)->create(['status' => ConsultationStatus::Completed]); // Create 2 no-shows = 20% rate Consultation::factory()->count(2)->create(['status' => ConsultationStatus::NoShow]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $bookingMetrics = $component->viewData('bookingMetrics'); expect($bookingMetrics['no_show_rate'])->toBe(20.0); }); test('no-show rate handles division by zero', function () { // No completed or no-show consultations Consultation::factory()->create(['status' => ConsultationStatus::Pending]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $bookingMetrics = $component->viewData('bookingMetrics'); expect($bookingMetrics['no_show_rate'])->toBe(0); }); test('this week consultations counts correctly', function () { // Create consultations this week Consultation::factory()->count(3)->create([ 'booking_date' => now()->startOfWeek()->addDays(2), 'status' => ConsultationStatus::Approved, ]); // Create consultations outside this week Consultation::factory()->count(2)->create([ 'booking_date' => now()->subWeeks(2), 'status' => ConsultationStatus::Approved, ]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $bookingMetrics = $component->viewData('bookingMetrics'); expect($bookingMetrics['this_week'])->toBe(3); }); test('this month consultations counts correctly', function () { // Create consultations this month Consultation::factory()->count(4)->create([ 'booking_date' => now()->startOfMonth()->addDays(5), ]); // Create consultations last month Consultation::factory()->count(2)->create([ 'booking_date' => now()->subMonth(), ]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $bookingMetrics = $component->viewData('bookingMetrics'); expect($bookingMetrics['this_month'])->toBe(4); }); // =========================================== // Timeline Metrics Tests // =========================================== test('dashboard displays correct timeline metrics', function () { Timeline::factory()->count(5)->create(['status' => TimelineStatus::Active]); Timeline::factory()->count(3)->create(['status' => TimelineStatus::Archived]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $timelineMetrics = $component->viewData('timelineMetrics'); expect($timelineMetrics['active'])->toBe(5) ->and($timelineMetrics['archived'])->toBe(3); }); test('timeline updates this week counts correctly', function () { $timeline = Timeline::factory()->create(); // Create updates this week TimelineUpdate::factory()->count(4)->create([ 'timeline_id' => $timeline->id, 'created_at' => now()->subDays(3), ]); // Create updates older than a week TimelineUpdate::factory()->count(2)->create([ 'timeline_id' => $timeline->id, 'created_at' => now()->subWeeks(2), ]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $timelineMetrics = $component->viewData('timelineMetrics'); expect($timelineMetrics['updates_this_week'])->toBe(4); }); // =========================================== // Post Metrics Tests // =========================================== test('dashboard displays correct post metrics', function () { Post::factory()->count(5)->published()->create(); Post::factory()->count(3)->draft()->create(); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $postMetrics = $component->viewData('postMetrics'); expect($postMetrics['total_published'])->toBe(5); }); test('posts published this month counts correctly', function () { // Create posts published this month Post::factory()->count(3)->published()->create([ 'published_at' => now(), ]); // Create posts published last month Post::factory()->count(2)->published()->create([ 'published_at' => now()->subMonth(), ]); $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $postMetrics = $component->viewData('postMetrics'); expect($postMetrics['this_month'])->toBe(3); }); // =========================================== // Empty State Tests // =========================================== test('dashboard handles empty database gracefully', function () { $this->actingAs($this->admin) ->get(route('admin.dashboard')) ->assertSuccessful() ->assertSee('0'); }); test('all metrics show zero when no data exists', function () { $this->actingAs($this->admin); $component = Volt::test('admin.dashboard'); $userMetrics = $component->viewData('userMetrics'); $bookingMetrics = $component->viewData('bookingMetrics'); $timelineMetrics = $component->viewData('timelineMetrics'); $postMetrics = $component->viewData('postMetrics'); expect($userMetrics['total_active'])->toBe(0) ->and($userMetrics['individual'])->toBe(0) ->and($userMetrics['company'])->toBe(0) ->and($userMetrics['deactivated'])->toBe(0) ->and($userMetrics['new_this_month'])->toBe(0) ->and($bookingMetrics['pending'])->toBe(0) ->and($bookingMetrics['today'])->toBe(0) ->and($bookingMetrics['this_week'])->toBe(0) ->and($bookingMetrics['this_month'])->toBe(0) ->and($bookingMetrics['free'])->toBe(0) ->and($bookingMetrics['paid'])->toBe(0) ->and($bookingMetrics['no_show_rate'])->toBe(0) ->and($timelineMetrics['active'])->toBe(0) ->and($timelineMetrics['archived'])->toBe(0) ->and($timelineMetrics['updates_this_week'])->toBe(0) ->and($postMetrics['total_published'])->toBe(0) ->and($postMetrics['this_month'])->toBe(0); }); // =========================================== // Cache Tests // =========================================== test('metrics are cached for 5 minutes', function () { $this->actingAs($this->admin); // First load to populate cache Volt::test('admin.dashboard'); // Verify cache keys exist expect(Cache::has('admin.metrics.users'))->toBeTrue() ->and(Cache::has('admin.metrics.bookings'))->toBeTrue() ->and(Cache::has('admin.metrics.timelines'))->toBeTrue() ->and(Cache::has('admin.metrics.posts'))->toBeTrue(); }); test('cached metrics persist after data changes', function () { $this->actingAs($this->admin); // First load to populate cache $component = Volt::test('admin.dashboard'); $initialCount = $component->viewData('userMetrics')['total_active']; // Add new users User::factory()->count(5)->individual()->create(['status' => UserStatus::Active]); // Reload component - should still show cached value $component = Volt::test('admin.dashboard'); $cachedCount = $component->viewData('userMetrics')['total_active']; expect($cachedCount)->toBe($initialCount); }); // =========================================== // UI Element Tests // =========================================== test('dashboard displays all metric cards', function () { $this->actingAs($this->admin) ->get(route('admin.dashboard')) ->assertSee(__('admin_metrics.clients')) ->assertSee(__('admin_metrics.consultations')) ->assertSee(__('admin_metrics.timelines')) ->assertSee(__('admin_metrics.posts')); }); test('pending requests badge is displayed', function () { Consultation::factory()->count(5)->create(['status' => ConsultationStatus::Pending]); $this->actingAs($this->admin) ->get(route('admin.dashboard')) ->assertSee(__('admin_metrics.pending_requests')); });