admin = User::factory()->admin()->create(); }); // =========================================== // Authentication Tests // =========================================== test('audit log page requires admin authentication', function () { $this->get(route('admin.audit-logs')) ->assertRedirect(route('login')); }); test('client cannot access audit logs page', function () { $client = User::factory()->individual()->create(); $this->actingAs($client) ->get(route('admin.audit-logs')) ->assertForbidden(); }); test('audit log page loads for admin', function () { $this->actingAs($this->admin) ->get(route('admin.audit-logs')) ->assertOk() ->assertSeeLivewire('admin.audit-logs'); }); // =========================================== // Display Tests // =========================================== test('audit logs display in table', function () { AdminLog::factory()->count(5)->create(); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->assertSee(__('audit.timestamp')) ->assertSee(__('audit.admin')) ->assertSee(__('audit.action')); }); test('displays empty state when no logs exist', function () { $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->assertSee(__('audit.no_logs_found')); }); test('pagination works correctly with 25 items per page', function () { AdminLog::factory()->count(50)->create(); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->assertViewHas('logs', fn ($logs) => $logs->count() === 25); }); test('logs are sorted by created_at descending', function () { $oldLog = AdminLog::factory()->create(['created_at' => now()->subDays(5)]); $newLog = AdminLog::factory()->create(['created_at' => now()]); $this->actingAs($this->admin); $component = Volt::test('admin.audit-logs'); $logs = $component->viewData('logs'); expect($logs->first()->id)->toBe($newLog->id); }); test('displays system for logs without admin', function () { AdminLog::factory()->create(['admin_id' => null]); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->assertSee(__('audit.system')); }); // =========================================== // Filter Tests // =========================================== test('can filter by action type', function () { AdminLog::factory()->create(['action' => 'create']); AdminLog::factory()->create(['action' => 'delete']); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->set('actionFilter', 'create') ->assertViewHas('logs', fn ($logs) => $logs->every(fn ($log) => $log->action === 'create')); }); test('can filter by target type', function () { AdminLog::factory()->create(['target_type' => 'user']); AdminLog::factory()->create(['target_type' => 'consultation']); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->set('targetFilter', 'user') ->assertViewHas('logs', fn ($logs) => $logs->every(fn ($log) => $log->target_type === 'user')); }); test('can filter by date range', function () { AdminLog::factory()->create(['created_at' => now()->subDays(10)]); AdminLog::factory()->create(['created_at' => now()]); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->set('dateFrom', now()->subDays(5)->format('Y-m-d')) ->set('dateTo', now()->format('Y-m-d')) ->assertViewHas('logs', fn ($logs) => $logs->count() === 1); }); test('can search by target id', function () { AdminLog::factory()->create(['target_id' => 123]); AdminLog::factory()->create(['target_id' => 456]); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->set('search', '123') ->assertViewHas('logs', fn ($logs) => $logs->count() === 1 && $logs->first()->target_id === 123); }); test('reset filters clears all filters', function () { $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->set('actionFilter', 'create') ->set('targetFilter', 'user') ->set('dateFrom', '2024-01-01') ->set('dateTo', '2024-12-31') ->set('search', '123') ->call('resetFilters') ->assertSet('actionFilter', '') ->assertSet('targetFilter', '') ->assertSet('dateFrom', '') ->assertSet('dateTo', '') ->assertSet('search', ''); }); test('filters reset pagination', function () { AdminLog::factory()->count(30)->create(['action' => 'create']); AdminLog::factory()->count(30)->create(['action' => 'delete']); $this->actingAs($this->admin); $component = Volt::test('admin.audit-logs') ->call('gotoPage', 2) ->set('actionFilter', 'create'); // After setting filter, should be back to page 1 $logs = $component->viewData('logs'); expect($logs->currentPage())->toBe(1); }); test('displays empty state when filters return no results', function () { AdminLog::factory()->create(['action' => 'create']); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->set('actionFilter', 'delete') ->assertSee(__('audit.no_results')); }); // =========================================== // Modal Tests // =========================================== test('can view log details in modal', function () { $log = AdminLog::factory()->create([ 'old_values' => ['name' => 'Old Name'], 'new_values' => ['name' => 'New Name'], ]); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->call('showDetails', $log->id) ->assertSet('selectedLogId', $log->id) ->assertDispatched('open-modal', name: 'log-details'); }); test('selected log is loaded with admin relation', function () { $log = AdminLog::factory()->create(); $this->actingAs($this->admin); $component = Volt::test('admin.audit-logs') ->call('showDetails', $log->id); $selectedLog = $component->viewData('selectedLog'); expect($selectedLog)->not->toBeNull(); expect($selectedLog->id)->toBe($log->id); }); test('modal displays old and new values', function () { $log = AdminLog::factory()->create([ 'old_values' => ['status' => 'pending'], 'new_values' => ['status' => 'approved'], ]); $this->actingAs($this->admin); $component = Volt::test('admin.audit-logs') ->call('showDetails', $log->id); $selectedLog = $component->viewData('selectedLog'); expect($selectedLog->old_values)->toBe(['status' => 'pending']); expect($selectedLog->new_values)->toBe(['status' => 'approved']); }); test('can close modal', function () { $log = AdminLog::factory()->create(); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->call('showDetails', $log->id) ->assertSet('selectedLogId', $log->id) ->call('closeModal') ->assertSet('selectedLogId', null) ->assertDispatched('close-modal', name: 'log-details'); }); // =========================================== // CSV Export Tests // =========================================== test('can export filtered logs to csv', function () { AdminLog::factory()->count(3)->create(); $this->actingAs($this->admin); Volt::test('admin.audit-logs') ->call('exportCsv') ->assertFileDownloaded(); }); test('csv export respects filters', function () { AdminLog::factory()->count(3)->create(['action' => 'create']); AdminLog::factory()->count(2)->create(['action' => 'delete']); $this->actingAs($this->admin); // Set filter before export $component = Volt::test('admin.audit-logs') ->set('actionFilter', 'create'); // Export is tested by checking it doesn't error and downloads $component->call('exportCsv') ->assertFileDownloaded(); }); test('csv filename includes current date', function () { AdminLog::factory()->create(); $this->actingAs($this->admin); $response = Volt::test('admin.audit-logs') ->call('exportCsv'); // The response should be a StreamedResponse $response->assertFileDownloaded('audit-log-'.now()->format('Y-m-d').'.csv'); }); // =========================================== // Action Types and Target Types // =========================================== test('action types are populated from database', function () { AdminLog::factory()->create(['action' => 'create']); AdminLog::factory()->create(['action' => 'update']); AdminLog::factory()->create(['action' => 'delete']); $this->actingAs($this->admin); $component = Volt::test('admin.audit-logs'); $actionTypes = $component->viewData('actionTypes'); expect($actionTypes)->toContain('create', 'update', 'delete'); }); test('target types are populated from database', function () { AdminLog::factory()->create(['target_type' => 'user']); AdminLog::factory()->create(['target_type' => 'consultation']); $this->actingAs($this->admin); $component = Volt::test('admin.audit-logs'); $targetTypes = $component->viewData('targetTypes'); expect($targetTypes)->toContain('user', 'consultation'); }); // =========================================== // Locale-specific Tests // =========================================== test('timestamp format changes based on locale', function () { $log = AdminLog::factory()->create(['created_at' => '2024-06-15 10:30:00']); $this->actingAs($this->admin); // English locale shows m/d/Y format app()->setLocale('en'); Volt::test('admin.audit-logs') ->assertSee('06/15/2024'); // Arabic locale shows d/m/Y format app()->setLocale('ar'); Volt::test('admin.audit-logs') ->assertSee('15/06/2024'); });