admin = User::factory()->admin()->create(); }); // =========================================== // Happy Path Tests // =========================================== test('admin can view timeline creation form', function () { $this->actingAs($this->admin) ->get(route('admin.timelines.create')) ->assertOk(); }); test('admin can search clients by name', function () { $client = User::factory()->individual()->create(['full_name' => 'John Doe']); User::factory()->individual()->create(['full_name' => 'Jane Smith']); $this->actingAs($this->admin); $component = Volt::test('admin.timelines.create') ->set('search', 'John'); expect($component->get('clients'))->toHaveCount(1); expect($component->get('clients')->first()->full_name)->toBe('John Doe'); }); test('admin can search clients by email', function () { $client = User::factory()->individual()->create([ 'full_name' => 'John Doe', 'email' => 'john@example.com', ]); User::factory()->individual()->create([ 'full_name' => 'Jane Smith', 'email' => 'jane@example.com', ]); $this->actingAs($this->admin); $component = Volt::test('admin.timelines.create') ->set('search', 'john@'); expect($component->get('clients'))->toHaveCount(1); expect($component->get('clients')->first()->email)->toBe('john@example.com'); }); test('admin can create timeline with required fields', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Test Case') ->call('create') ->assertHasNoErrors() ->assertRedirect(); expect(Timeline::where('case_name', 'Test Case')->exists())->toBeTrue(); $timeline = Timeline::where('case_name', 'Test Case')->first(); expect($timeline->user_id)->toBe($client->id); expect($timeline->status)->toBe(TimelineStatus::Active); expect($timeline->case_reference)->toBeNull(); }); test('admin can create timeline with case reference', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Test Case') ->set('caseReference', 'REF-001') ->call('create') ->assertHasNoErrors(); $timeline = Timeline::where('case_name', 'Test Case')->first(); expect($timeline->case_reference)->toBe('REF-001'); }); test('initial notes creates first timeline update', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Test Case') ->set('initialNotes', 'Initial case notes here') ->call('create') ->assertHasNoErrors(); $timeline = Timeline::where('case_name', 'Test Case')->first(); expect($timeline->updates)->toHaveCount(1); $update = $timeline->updates->first(); expect($update->update_text)->toBe('Initial case notes here'); expect($update->admin_id)->toBe($this->admin->id); }); test('audit log created on timeline creation', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Test Case') ->call('create') ->assertHasNoErrors(); expect(AdminLog::where('action', 'create') ->where('target_type', 'timeline') ->where('admin_id', $this->admin->id) ->exists())->toBeTrue(); }); // =========================================== // Validation Tests // =========================================== test('case name is required', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', '') ->call('create') ->assertHasErrors(['caseName' => 'required']); }); test('case reference must be unique', function () { $client = User::factory()->individual()->create(); Timeline::factory()->create(['case_reference' => 'REF-001']); $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Test Case') ->set('caseReference', 'REF-001') ->call('create') ->assertHasErrors(['caseReference' => 'unique']); }); test('case reference allows multiple nulls', function () { $client = User::factory()->individual()->create(); // Create a timeline without case_reference Timeline::factory()->create([ 'user_id' => $client->id, 'case_reference' => null, ]); $this->actingAs($this->admin); // Creating another timeline without case_reference should succeed Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Test Case') ->set('caseReference', '') ->call('create') ->assertHasNoErrors(); expect(Timeline::where('user_id', $client->id)->whereNull('case_reference')->count())->toBe(2); }); test('client selection is required', function () { $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('caseName', 'Test Case') ->call('create') ->assertHasErrors(['selectedUserId' => 'required']); }); test('selected client must exist', function () { $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', 99999) ->set('caseName', 'Test Case') ->call('create') ->assertHasErrors(['selectedUserId' => 'exists']); }); // =========================================== // Authorization Tests // =========================================== test('non-admin cannot access timeline creation', function () { $client = User::factory()->individual()->create(); $this->actingAs($client) ->get(route('admin.timelines.create')) ->assertForbidden(); }); test('guest cannot access timeline creation', function () { $this->get(route('admin.timelines.create')) ->assertRedirect(route('login')); }); // =========================================== // Edge Case Tests // =========================================== test('search only returns individual and company users', function () { $individualClient = User::factory()->individual()->create(['full_name' => 'Individual Client']); $companyClient = User::factory()->company()->create(['full_name' => 'Company Client']); $adminUser = User::factory()->admin()->create(['full_name' => 'Admin User']); $this->actingAs($this->admin); $component = Volt::test('admin.timelines.create') ->set('search', 'Client'); $clientNames = $component->get('clients')->pluck('full_name')->toArray(); expect($clientNames)->toContain('Individual Client'); expect($clientNames)->toContain('Company Client'); expect($clientNames)->not->toContain('Admin User'); }); test('search only returns active users', function () { $activeClient = User::factory()->individual()->create(['full_name' => 'Active Client']); $deactivatedClient = User::factory()->individual()->deactivated()->create(['full_name' => 'Deactivated Client']); $this->actingAs($this->admin); $component = Volt::test('admin.timelines.create') ->set('search', 'Client'); $clientNames = $component->get('clients')->pluck('full_name')->toArray(); expect($clientNames)->toContain('Active Client'); expect($clientNames)->not->toContain('Deactivated Client'); }); test('can create multiple timelines for same client', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); // Create first timeline Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'First Case') ->call('create') ->assertHasNoErrors(); // Create second timeline for same client Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Second Case') ->call('create') ->assertHasNoErrors(); expect(Timeline::where('user_id', $client->id)->count())->toBe(2); }); // =========================================== // Additional Tests // =========================================== test('search requires at least 2 characters', function () { User::factory()->individual()->create(['full_name' => 'John Doe']); $this->actingAs($this->admin); $component = Volt::test('admin.timelines.create') ->set('search', 'J'); expect($component->get('clients'))->toHaveCount(0); $component->set('search', 'Jo'); expect($component->get('clients'))->toHaveCount(1); }); test('selecting user sets search to user name', function () { $client = User::factory()->individual()->create(['full_name' => 'John Doe']); $this->actingAs($this->admin); $component = Volt::test('admin.timelines.create') ->set('search', 'John') ->call('selectUser', $client->id); expect($component->get('selectedUserId'))->toBe($client->id); expect($component->get('search'))->toBe('John Doe'); }); test('clearing selection resets user and search', function () { $client = User::factory()->individual()->create(['full_name' => 'John Doe']); $this->actingAs($this->admin); $component = Volt::test('admin.timelines.create') ->call('selectUser', $client->id) ->call('clearSelection'); expect($component->get('selectedUserId'))->toBeNull(); expect($component->get('selectedUser'))->toBeNull(); expect($component->get('search'))->toBe(''); }); test('timeline without initial notes has no updates', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Test Case') ->set('initialNotes', '') ->call('create') ->assertHasNoErrors(); $timeline = Timeline::where('case_name', 'Test Case')->first(); expect($timeline->updates)->toHaveCount(0); }); test('timeline status defaults to active', function () { $client = User::factory()->individual()->create(); $this->actingAs($this->admin); Volt::test('admin.timelines.create') ->set('selectedUserId', $client->id) ->call('selectUser', $client->id) ->set('caseName', 'Test Case') ->call('create') ->assertHasNoErrors(); $timeline = Timeline::where('case_name', 'Test Case')->first(); expect($timeline->status)->toBe(TimelineStatus::Active); });