354 lines
11 KiB
PHP
354 lines
11 KiB
PHP
<?php
|
|
|
|
use App\Models\Timeline;
|
|
use App\Models\TimelineUpdate;
|
|
use App\Models\User;
|
|
use Livewire\Volt\Volt;
|
|
|
|
beforeEach(function () {
|
|
$this->admin = User::factory()->admin()->create();
|
|
});
|
|
|
|
// ===========================================
|
|
// Access Tests
|
|
// ===========================================
|
|
|
|
test('admin can access timeline export page', function () {
|
|
$this->actingAs($this->admin)
|
|
->get(route('admin.timelines.export'))
|
|
->assertOk();
|
|
});
|
|
|
|
test('non-admin cannot access timeline export page', function () {
|
|
$client = User::factory()->individual()->create();
|
|
|
|
$this->actingAs($client)
|
|
->get(route('admin.timelines.export'))
|
|
->assertForbidden();
|
|
});
|
|
|
|
test('unauthenticated user cannot access timeline export page', function () {
|
|
$this->get(route('admin.timelines.export'))
|
|
->assertRedirect(route('login'));
|
|
});
|
|
|
|
// ===========================================
|
|
// CSV Export Tests
|
|
// ===========================================
|
|
|
|
test('admin can export all timelines as CSV', function () {
|
|
Timeline::factory()->count(5)->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'all')
|
|
->call('exportCsv')
|
|
->assertFileDownloaded('timelines-export-'.now()->format('Y-m-d').'.csv');
|
|
});
|
|
|
|
test('admin can export timelines filtered by active status', function () {
|
|
Timeline::factory()->count(3)->active()->create();
|
|
Timeline::factory()->count(2)->archived()->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'active')
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
test('admin can export timelines filtered by archived status', function () {
|
|
Timeline::factory()->count(3)->active()->create();
|
|
Timeline::factory()->count(2)->archived()->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'archived')
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
test('admin can export timelines filtered by client', function () {
|
|
$client1 = User::factory()->individual()->create();
|
|
$client2 = User::factory()->individual()->create();
|
|
|
|
Timeline::factory()->count(3)->create(['user_id' => $client1->id]);
|
|
Timeline::factory()->count(2)->create(['user_id' => $client2->id]);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('clientId', $client1->id)
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
test('admin can export timelines filtered by date range', function () {
|
|
Timeline::factory()->create(['created_at' => now()->subDays(10)]);
|
|
Timeline::factory()->create(['created_at' => now()->subDays(5)]);
|
|
Timeline::factory()->create(['created_at' => now()]);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('dateFrom', now()->subDays(7)->format('Y-m-d'))
|
|
->set('dateTo', now()->format('Y-m-d'))
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
test('admin can export with combined filters', function () {
|
|
$client = User::factory()->individual()->create();
|
|
|
|
Timeline::factory()->active()->create(['user_id' => $client->id, 'created_at' => now()->subDays(5)]);
|
|
Timeline::factory()->archived()->create(['user_id' => $client->id, 'created_at' => now()->subDays(5)]);
|
|
Timeline::factory()->active()->create(['created_at' => now()->subDays(5)]);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('clientId', $client->id)
|
|
->set('status', 'active')
|
|
->set('dateFrom', now()->subDays(7)->format('Y-m-d'))
|
|
->set('dateTo', now()->format('Y-m-d'))
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
// ===========================================
|
|
// PDF Export Tests
|
|
// ===========================================
|
|
|
|
test('admin can export timelines as PDF', function () {
|
|
Timeline::factory()->count(5)->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->call('exportPdf')
|
|
->assertFileDownloaded('timelines-export-'.now()->format('Y-m-d').'.pdf');
|
|
});
|
|
|
|
test('admin can export PDF with filters', function () {
|
|
Timeline::factory()->count(3)->active()->create();
|
|
Timeline::factory()->count(2)->archived()->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'active')
|
|
->call('exportPdf')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
test('include updates toggle adds update content to PDF', function () {
|
|
$timeline = Timeline::factory()->create();
|
|
TimelineUpdate::factory()->count(3)->create(['timeline_id' => $timeline->id]);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('includeUpdates', true)
|
|
->call('exportPdf')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
// ===========================================
|
|
// Empty Dataset Tests
|
|
// ===========================================
|
|
|
|
test('CSV export dispatches notification when no timelines match filters', function () {
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'archived')
|
|
->call('exportCsv')
|
|
->assertDispatched('notify');
|
|
});
|
|
|
|
test('PDF export dispatches notification when no timelines match filters', function () {
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'archived')
|
|
->call('exportPdf')
|
|
->assertDispatched('notify');
|
|
});
|
|
|
|
test('preview count shows zero when no timelines match filters', function () {
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'archived')
|
|
->assertSee('0');
|
|
});
|
|
|
|
// ===========================================
|
|
// Filter Tests
|
|
// ===========================================
|
|
|
|
test('preview count updates when filters change', function () {
|
|
Timeline::factory()->count(3)->active()->create();
|
|
Timeline::factory()->count(2)->archived()->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
// All timelines - should show 5
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'all')
|
|
->assertSeeHtml('<span class="font-semibold text-zinc-900 dark:text-zinc-100">5</span>');
|
|
|
|
// Filter to active only - should show 3
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'active')
|
|
->assertSeeHtml('<span class="font-semibold text-zinc-900 dark:text-zinc-100">3</span>');
|
|
|
|
// Filter to archived only - should show 2
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->set('status', 'archived')
|
|
->assertSeeHtml('<span class="font-semibold text-zinc-900 dark:text-zinc-100">2</span>');
|
|
});
|
|
|
|
test('clear filters resets all filter values', function () {
|
|
$client = User::factory()->individual()->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
$component = Volt::test('admin.timelines.export-timelines')
|
|
->set('clientId', $client->id)
|
|
->set('clientSearch', $client->full_name)
|
|
->set('status', 'archived')
|
|
->set('dateFrom', '2024-01-01')
|
|
->set('dateTo', '2024-12-31')
|
|
->set('includeUpdates', true)
|
|
->call('clearFilters');
|
|
|
|
expect($component->get('clientId'))->toBeNull();
|
|
expect($component->get('clientSearch'))->toBe('');
|
|
expect($component->get('status'))->toBe('all');
|
|
expect($component->get('dateFrom'))->toBe('');
|
|
expect($component->get('dateTo'))->toBe('');
|
|
expect($component->get('includeUpdates'))->toBeFalse();
|
|
});
|
|
|
|
// ===========================================
|
|
// Client Search Tests
|
|
// ===========================================
|
|
|
|
test('client search returns matching clients', function () {
|
|
$client1 = User::factory()->individual()->create(['full_name' => 'John Doe']);
|
|
$client2 = User::factory()->individual()->create(['full_name' => 'Jane Smith']);
|
|
User::factory()->admin()->create(['full_name' => 'Admin User']);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
$component = Volt::test('admin.timelines.export-timelines')
|
|
->set('clientSearch', 'John');
|
|
|
|
expect($component->get('clients'))->toHaveCount(1);
|
|
expect($component->get('clients')->first()->id)->toBe($client1->id);
|
|
});
|
|
|
|
test('client search excludes admin users', function () {
|
|
User::factory()->admin()->create(['full_name' => 'Admin John']);
|
|
$client = User::factory()->individual()->create(['full_name' => 'Client John']);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
$component = Volt::test('admin.timelines.export-timelines')
|
|
->set('clientSearch', 'John');
|
|
|
|
expect($component->get('clients'))->toHaveCount(1);
|
|
expect($component->get('clients')->first()->id)->toBe($client->id);
|
|
});
|
|
|
|
test('client search requires minimum 2 characters', function () {
|
|
User::factory()->individual()->create(['full_name' => 'John Doe']);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
$component = Volt::test('admin.timelines.export-timelines')
|
|
->set('clientSearch', 'J');
|
|
|
|
expect($component->get('clients'))->toBeEmpty();
|
|
});
|
|
|
|
test('select client sets client ID and search field', function () {
|
|
$client = User::factory()->individual()->create(['full_name' => 'John Doe']);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
$component = Volt::test('admin.timelines.export-timelines')
|
|
->call('selectClient', $client->id);
|
|
|
|
expect($component->get('clientId'))->toBe($client->id);
|
|
expect($component->get('clientSearch'))->toBe('John Doe');
|
|
});
|
|
|
|
test('clear client resets client filter', function () {
|
|
$client = User::factory()->individual()->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
$component = Volt::test('admin.timelines.export-timelines')
|
|
->set('clientId', $client->id)
|
|
->set('clientSearch', $client->full_name)
|
|
->call('clearClient');
|
|
|
|
expect($component->get('clientId'))->toBeNull();
|
|
expect($component->get('clientSearch'))->toBe('');
|
|
});
|
|
|
|
// ===========================================
|
|
// Timeline Data Tests
|
|
// ===========================================
|
|
|
|
test('timeline without updates shows zero count', function () {
|
|
Timeline::factory()->create();
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
test('timeline without case reference shows dash in export', function () {
|
|
Timeline::factory()->create(['case_reference' => null]);
|
|
|
|
$this->actingAs($this->admin);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
// ===========================================
|
|
// Language Tests
|
|
// ===========================================
|
|
|
|
test('export uses admin preferred language for headers', function () {
|
|
$adminArabic = User::factory()->admin()->create(['preferred_language' => 'ar']);
|
|
Timeline::factory()->create();
|
|
|
|
$this->actingAs($adminArabic);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|
|
|
|
test('export uses English when admin prefers English', function () {
|
|
$adminEnglish = User::factory()->admin()->create(['preferred_language' => 'en']);
|
|
Timeline::factory()->create();
|
|
|
|
$this->actingAs($adminEnglish);
|
|
|
|
Volt::test('admin.timelines.export-timelines')
|
|
->call('exportCsv')
|
|
->assertFileDownloaded();
|
|
});
|