presseportale/tests/Feature/Admin/UserManagementTest.php
Kevin Adametz e8c47b7553
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
22-05-2026 Optimierung der User und Admin Panels
2026-05-22 11:18:59 +02:00

1311 lines
45 KiB
PHP

<?php
use App\Models\Company;
use App\Models\Contact;
use App\Models\PressRelease;
use App\Models\Profile;
use App\Models\User;
use App\Models\UserFilterPreset;
use Database\Seeders\RolesAndPermissionsSeeder;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Storage;
use Livewire\Volt\Volt as LivewireVolt;
use Tests\TestCase;
test('admin can update user roles companies contacts and billing address', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$targetUser = User::factory()->create([
'name' => 'Alt Benutzer',
'email' => 'alt-user@example.com',
'is_active' => true,
]);
$targetUser->assignRole('customer');
Profile::factory()->for($targetUser)->create([
'salutation_key' => 'mr',
'title' => 'Dr.',
'first_name' => 'Alter',
'last_name' => 'Name',
'phone' => '030123',
'address' => 'Alte Strasse 1',
'country_code' => 'DE',
'backlink_url' => 'https://alt.example.com',
]);
$companyA = Company::query()->create([
'name' => 'Alpha GmbH',
'slug' => 'alpha-gmbh',
]);
$companyB = Company::query()->create([
'name' => 'Beta GmbH',
'slug' => 'beta-gmbh',
]);
$targetUser->companies()->attach($companyA->id, ['role' => 'member']);
$contact = Contact::query()->create([
'company_id' => $companyA->id,
'first_name' => 'Max',
'last_name' => 'Muster',
'email' => 'max@example.com',
'phone' => '0123',
'responsibility' => 'Presse',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users.edit', ['id' => $targetUser->id])
->set('name', 'Neu Benutzer')
->set('email', 'neu-user@example.com')
->set('profile.salutation_key', 'mrs')
->set('profile.title', 'Prof.')
->set('profile.first_name', 'Erika')
->set('profile.last_name', 'Profil')
->set('profile.phone', '089123456')
->set('profile.address', 'Profilstrasse 5')
->set('profile.country_code', 'AT')
->set('profile.backlink_url', 'https://neu.example.com')
->set('profile.show_stats', true)
->set('profile.tax_id_number', 'ATU12345678')
->set('companyLookup', 'Alpha')
->call('clearCompanyLookup')
->assertSet('companyLookup', '')
->assertSet('selectedLookupCompanyId', null)
->set('contactLookup', 'Max')
->call('clearContactLookup')
->assertSet('contactLookup', '')
->assertSet('selectedLookupContactId', null)
->set('selectedRoles', ['editor'])
->set('linkedCompanyIds', [$companyA->id, $companyB->id])
->set("companyRoles.$companyA->id", 'responsible')
->set("companyRoles.$companyB->id", 'owner')
->set('contactForms.0.first_name', 'Erika')
->set('contactForms.0.last_name', 'Mustermann')
->set('contactForms.0.email', 'erika@example.com')
->set('contactForms.0.phone', '0999')
->set('contactForms.0.responsibility', 'Leitung PR')
->set('billing.salutation_key', 'mrs')
->set('billing.name', 'Neu Benutzer GmbH')
->set('billing.address1', 'Neue Strasse 10')
->set('billing.postal_code', '80331')
->set('billing.city', 'Muenchen')
->set('billing.country_code', 'DE')
->call('save')
->assertHasNoErrors();
$targetUser->refresh();
$contact->refresh();
expect($targetUser->name)->toBe('Neu Benutzer');
expect($targetUser->email)->toBe('neu-user@example.com');
expect($targetUser->hasRole('editor'))->toBeTrue();
expect($targetUser->hasRole('customer'))->toBeFalse();
$profile = $targetUser->profile()->first();
expect($profile)->not->toBeNull();
expect($profile?->salutation_key)->toBe('mrs');
expect($profile?->title)->toBe('Prof.');
expect($profile?->first_name)->toBe('Erika');
expect($profile?->last_name)->toBe('Profil');
expect($profile?->phone)->toBe('089123456');
expect($profile?->address)->toBe('Profilstrasse 5');
expect($profile?->country_code)->toBe('AT');
expect($profile?->backlink_url)->toBe('https://neu.example.com');
expect($profile?->show_stats)->toBeTrue();
expect($profile?->tax_id_number)->toBe('ATU12345678');
expect($targetUser->companies()->whereKey($companyA->id)->exists())->toBeTrue();
expect($targetUser->companies()->whereKey($companyB->id)->exists())->toBeTrue();
expect($targetUser->companies()->where('companies.id', $companyA->id)->first()?->pivot?->role)->toBe('responsible');
expect($targetUser->companies()->where('companies.id', $companyB->id)->first()?->pivot?->role)->toBe('owner');
expect($contact->first_name)->toBe('Erika');
expect($contact->last_name)->toBe('Mustermann');
expect($contact->email)->toBe('erika@example.com');
expect($contact->phone)->toBe('0999');
expect($contact->responsibility)->toBe('Leitung PR');
$billingAddress = $targetUser->billingAddress()->first();
expect($billingAddress)->not->toBeNull();
expect($billingAddress?->name)->toBe('Neu Benutzer GmbH');
expect($billingAddress?->address1)->toBe('Neue Strasse 10');
expect($billingAddress?->postal_code)->toBe('80331');
expect($billingAddress?->city)->toBe('Muenchen');
expect($billingAddress?->country_code)->toBe('DE');
});
test('admin user edit keeps empty profile date fields empty', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$targetUser = User::factory()->create([
'name' => 'Datum Leer',
'email' => 'datum-leer@example.com',
]);
$targetUser->assignRole('customer');
Profile::factory()->for($targetUser)->create([
'birthdate' => null,
'validation_date' => null,
'contract_date' => null,
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users.edit', ['id' => $targetUser->id])
->assertSee('Geburtsdatum')
->assertSee('Validierungsdatum')
->assertSee('Vertragsdatum')
->assertSet('profile.birthdate', '')
->assertSet('profile.validation_date', '')
->assertSet('profile.contract_date', '')
->call('save')
->assertHasNoErrors();
$profile = $targetUser->profile()->first();
expect($profile?->birthdate)->toBeNull();
expect($profile?->validation_date)->toBeNull();
expect($profile?->contract_date)->toBeNull();
});
test('admin can create user with roles companies and optional billing address', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$companyA = Company::query()->create([
'name' => 'Gamma GmbH',
'slug' => 'gamma-gmbh',
]);
$companyB = Company::query()->create([
'name' => 'Delta GmbH',
'slug' => 'delta-gmbh',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users.create')
->set('name', 'Neuer User')
->set('email', 'new-user@example.com')
->set('selectedRoles', ['customer'])
->set('linkedCompanyIds', [$companyA->id, $companyB->id])
->set("companyRoles.$companyA->id", 'member')
->set("companyRoles.$companyB->id", 'owner')
->set('billing.name', 'Neuer User Billing')
->set('billing.address1', 'Hauptstrasse 1')
->set('billing.postal_code', '20095')
->set('billing.city', 'Hamburg')
->set('billing.country_code', 'DE')
->call('save')
->assertHasNoErrors();
$createdUser = User::query()->where('email', 'new-user@example.com')->firstOrFail();
expect($createdUser->name)->toBe('Neuer User');
expect($createdUser->hasRole('customer'))->toBeTrue();
expect($createdUser->companies()->whereKey($companyA->id)->exists())->toBeTrue();
expect($createdUser->companies()->whereKey($companyB->id)->exists())->toBeTrue();
expect($createdUser->companies()->where('companies.id', $companyB->id)->first()?->pivot?->role)->toBe('owner');
$billingAddress = $createdUser->billingAddress()->first();
expect($billingAddress)->not->toBeNull();
expect($billingAddress?->name)->toBe('Neuer User Billing');
});
test('admin can link companies and contacts in user edit via live lookup flow', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$user = User::factory()->create([
'name' => 'Lookup User',
'email' => 'lookup-user@example.com',
]);
$user->assignRole('customer');
$companyA = Company::query()->create([
'name' => 'Lookup Alpha GmbH',
'slug' => 'lookup-alpha-gmbh',
]);
$companyB = Company::query()->create([
'name' => 'Lookup Beta GmbH',
'slug' => 'lookup-beta-gmbh',
]);
$contact = Contact::query()->create([
'company_id' => $companyA->id,
'first_name' => 'Lena',
'last_name' => 'Lookup',
'email' => 'lena@lookup-alpha.test',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users.edit', ['id' => $user->id])
->set('companyLookup', 'Alpha')
->set('selectedLookupCompanyId', $companyA->id)
->call('addLinkedCompany')
->set('companyLookup', 'Beta')
->set('selectedLookupCompanyId', $companyB->id)
->call('addLinkedCompany')
->set('contactLookup', 'Lena')
->set('selectedLookupContactId', $contact->id)
->call('addLinkedContact')
->set("companyRoles.$companyA->id", 'owner')
->set("companyRoles.$companyB->id", 'member')
->call('save')
->assertHasNoErrors();
$user->refresh();
expect($user->companies()->whereKey($companyA->id)->exists())->toBeTrue();
expect($user->companies()->whereKey($companyB->id)->exists())->toBeTrue();
expect($user->companies()->where('companies.id', $companyA->id)->first()?->pivot?->role)->toBe('owner');
});
test('removing linked company in user edit is persisted immediately', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$user = User::factory()->create();
$user->assignRole('customer');
$companyA = Company::query()->create([
'name' => 'Persist Remove Alpha',
'slug' => 'persist-remove-alpha',
]);
$companyB = Company::query()->create([
'name' => 'Persist Remove Beta',
'slug' => 'persist-remove-beta',
]);
$user->companies()->attach($companyA->id, ['role' => 'member']);
$user->companies()->attach($companyB->id, ['role' => 'member']);
$this->actingAs($admin);
LivewireVolt::test('admin.users.edit', ['id' => $user->id])
->call('removeLinkedCompany', $companyB->id)
->assertHasNoErrors();
$user->refresh();
expect($user->companies()->whereKey($companyA->id)->exists())->toBeTrue();
expect($user->companies()->whereKey($companyB->id)->exists())->toBeFalse();
});
test('contact lookup in user edit finds results from unlinked companies and auto-links company', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$user = User::factory()->create();
$user->assignRole('customer');
$company = Company::query()->create([
'name' => 'Lookup Source',
'slug' => 'lookup-source',
]);
$contact = Contact::query()->create([
'company_id' => $company->id,
'first_name' => 'Sophie',
'last_name' => 'Finder',
'email' => 'sophie@lookup-source.test',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users.edit', ['id' => $user->id])
->set('contactLookup', 'Sophie')
->assertViewHas('contactLookupResults', fn ($results) => $results->pluck('id')->contains($contact->id))
->set('selectedLookupContactId', $contact->id)
->call('addLinkedContact')
->assertHasNoErrors();
$user->refresh();
expect($user->companies()->whereKey($company->id)->exists())->toBeTrue();
expect($user->contacts()->whereKey($contact->id)->exists())->toBeTrue();
});
test('adding linked company in user edit is persisted immediately', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$user = User::factory()->create();
$user->assignRole('customer');
$company = Company::query()->create([
'name' => 'Instant Add GmbH',
'slug' => 'instant-add-gmbh',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users.edit', ['id' => $user->id])
->set('companyLookup', 'Instant Add')
->set('selectedLookupCompanyId', $company->id)
->call('addLinkedCompany')
->assertHasNoErrors();
$user->refresh();
expect($user->companies()->whereKey($company->id)->exists())->toBeTrue();
});
test('removing linked contact in user edit is persisted immediately', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$user = User::factory()->create();
$user->assignRole('customer');
$company = Company::query()->create([
'name' => 'Contact Remove Co',
'slug' => 'contact-remove-co',
]);
$contactA = Contact::query()->create([
'company_id' => $company->id,
'first_name' => 'Aline',
'last_name' => 'Keep',
'email' => 'aline@remove.test',
]);
$contactB = Contact::query()->create([
'company_id' => $company->id,
'first_name' => 'Bela',
'last_name' => 'Remove',
'email' => 'bela@remove.test',
]);
$user->companies()->attach($company->id, ['role' => 'member']);
$user->contacts()->attach($contactA->id);
$user->contacts()->attach($contactB->id);
$this->actingAs($admin);
LivewireVolt::test('admin.users.edit', ['id' => $user->id])
->call('removeLinkedContact', $contactB->id)
->assertHasNoErrors();
$user->refresh();
expect($user->contacts()->whereKey($contactA->id)->exists())->toBeTrue();
expect($user->contacts()->whereKey($contactB->id)->exists())->toBeFalse();
});
test('admin can view user detail page with linked company and billing data', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$targetUser = User::factory()->create([
'name' => 'Detail User',
'email' => 'detail-user@example.com',
]);
$targetUser->assignRole('customer');
$company = Company::query()->create([
'name' => 'Show GmbH',
'slug' => 'show-gmbh',
]);
$targetUser->companies()->attach($company->id, ['role' => 'responsible']);
Contact::query()->create([
'company_id' => $company->id,
'first_name' => 'Lisa',
'last_name' => 'Kontakt',
'email' => 'lisa@show-gmbh.de',
'responsibility' => 'PR',
]);
$targetUser->billingAddress()->create([
'name' => 'Detail Billing',
'address1' => 'Musterweg 2',
'postal_code' => '50667',
'city' => 'Koeln',
'country_code' => 'DE',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users.show', ['id' => $targetUser->id])
->assertSee('Detail User')
->assertSee('Show GmbH')
->assertSee('Lisa Kontakt')
->assertSee('Detail Billing');
});
test('admin can open user details modal from users index and see company link state', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$targetUser = User::factory()->create([
'name' => 'Modal User',
'email' => 'modal-user@example.com',
]);
$targetUser->assignRole('customer');
$company = Company::query()->create([
'name' => 'Modal GmbH',
'slug' => 'modal-gmbh',
]);
$targetUser->companies()->attach($company->id, ['role' => 'owner']);
$contact = Contact::query()->create([
'company_id' => $company->id,
'first_name' => 'Mona',
'last_name' => 'Modal',
'email' => 'mona@modal-gmbh.de',
'responsibility' => 'Presse',
]);
$publishedPressRelease = PressRelease::factory()
->for($targetUser)
->for($company)
->published()
->create([
'title' => 'Modal Pressemitteilung',
'hits' => 42,
]);
$publishedPressRelease->contacts()->attach($contact->id);
PressRelease::factory()
->for($targetUser)
->for($company)
->create([
'title' => 'Modal Entwurf',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users')
->set('search', 'modal-user@example.com')
->assertSee('modal-user@example.com')
->assertSee('1 Firma')
->assertSee('1 veröffentlicht')
->call('showUserDetails', $targetUser->id)
->assertSet('viewingUserId', $targetUser->id)
->assertSee('Firmen, Kontakte und Pressemitteilungen')
->assertSee('Struktur: User')
->assertSee('Modal GmbH')
->assertSee('Mona Modal')
->assertSee('Inhaber')
->assertSee('Modal Pressemitteilung')
->assertSee('Veroeffentlicht')
->assertSee('Hits')
->assertSee('42')
->assertSee('veröffentlichte PM anzeigen')
->assertSee('/admin/press-releases?', false)
->assertSee('status=published', false)
->assertSee('user='.$targetUser->id, false);
LivewireVolt::test('admin.press-releases.index')
->assertViewHas('userLookupResults', fn ($results) => $results->isEmpty())
->assertViewHas('companyLookupResults', fn ($results) => $results->isEmpty())
->assertViewHas('contactLookupResults', fn ($results) => $results->isEmpty())
->set('userFilter', (string) $targetUser->id)
->set('statusFilter', 'published')
->assertSee('Modal Pressemitteilung')
->assertDontSee('Modal Entwurf')
->call('clearUserFilter')
->assertSet('userFilter', 'all')
->assertSet('userLookup', '')
->set('userFilter', (string) $targetUser->id)
->set('companyLookup', 'Modal')
->assertViewHas('companyLookupResults', fn ($results) => $results->pluck('id')->contains($company->id))
->set('companyFilter', (string) $company->id)
->assertSee('Modal Pressemitteilung')
->call('clearCompanyFilter')
->assertSet('companyFilter', 'all')
->assertSet('companyLookup', '')
->set('companyFilter', (string) $company->id)
->set('contactLookup', 'Mona')
->assertViewHas('contactLookupResults', fn ($results) => $results->pluck('id')->contains($contact->id))
->set('contactFilter', (string) $contact->id)
->assertSee('Modal Pressemitteilung')
->call('clearContactFilter')
->assertSet('contactFilter', 'all')
->assertSet('contactLookup', '');
});
test('admin users index free text search matches names and email parts', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
User::factory()->create([
'name' => 'Seabstian Einrock',
'email' => 'info@connectar.de',
])->assignRole('customer');
User::factory()->create([
'name' => 'Barbara Barr',
'email' => 'barbara@example.com',
])->assignRole('customer');
$this->actingAs($admin);
LivewireVolt::test('admin.users')
->set('search', 'Seabstian')
->assertSee('info@connectar.de')
->assertDontSee('barbara@example.com')
->set('search', 'Einrock')
->assertSee('info@connectar.de')
->assertDontSee('barbara@example.com')
->set('search', 'connectar.de')
->assertSee('Seabstian Einrock')
->assertDontSee('Barbara Barr')
->set('search', 'Seabstian Einrock info@connectar.de')
->assertSee('info@connectar.de')
->assertDontSee('barbara@example.com')
->set('search', 'Barr')
->assertSee('barbara@example.com')
->assertDontSee('info@connectar.de');
});
test('admin users index uses full count pagination', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
User::factory()->count(51)->create();
$this->actingAs($admin);
LivewireVolt::test('admin.users')
->assertViewHas('users', fn ($users): bool => $users instanceof LengthAwarePaginator
&& $users->perPage() === 50
&& $users->total() === 52
&& $users->lastPage() === 2);
});
test('admin users index supports workflow filters and quality badges', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$withoutCompany = User::factory()->create([
'name' => 'Ohne Firma User',
'email' => 'ohne-firma@example.com',
]);
$withoutCompany->assignRole('customer');
$completeUser = User::factory()->create([
'name' => 'Kompletter Workflow User',
'email' => 'komplett@example.com',
]);
$completeUser->assignRole('editor');
Profile::factory()->for($completeUser)->create([
'first_name' => 'Komplett',
'last_name' => 'Profil',
]);
$company = Company::query()->create([
'name' => 'Workflow GmbH',
'slug' => 'workflow-gmbh',
]);
$contact = Contact::query()->create([
'company_id' => $company->id,
'first_name' => 'Willi',
'last_name' => 'Workflow',
'email' => 'willi@workflow-gmbh.de',
]);
$completeUser->companies()->attach($company->id, ['role' => 'owner']);
$completeUser->contacts()->attach($contact->id);
$completeUser->billingAddress()->create([
'name' => 'Workflow Rechnung',
'address1' => 'Workflowweg 1',
'postal_code' => '10115',
'city' => 'Berlin',
'country_code' => 'DE',
]);
PressRelease::factory()
->for($completeUser)
->for($company)
->published()
->create([
'title' => 'Workflow veroeffentlicht',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.users')
->assertSee('Alle Datenstände')
->assertSee('Mit Pressemitteilungen')
->assertSee('Mit veröffentlichten PMs')
->assertSee('Profil fehlt')
->assertSee('Profil OK')
->assertSee('Rechnung OK')
->set('qualityFilter', 'without_company')
->assertSee('ohne-firma@example.com')
->assertDontSee('komplett@example.com')
->set('qualityFilter', 'with_profile')
->assertSee('komplett@example.com')
->assertDontSee('ohne-firma@example.com')
->set('qualityFilter', 'with_published_press_releases')
->assertSee('komplett@example.com')
->assertDontSee('ohne-firma@example.com')
->set('qualityFilter', 'without_published_press_releases')
->assertSee('ohne-firma@example.com')
->assertDontSee('komplett@example.com')
->set('qualityFilter', 'all')
->set('roleFilter', 'editor')
->assertSee('komplett@example.com')
->assertDontSee('ohne-firma@example.com')
->set('permissionFilter', 'can_publish_press_releases')
->assertSee('PM-Freigabe')
->assertSee('komplett@example.com')
->assertDontSee('ohne-firma@example.com');
});
test('admin can create and edit contacts via admin contact forms', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$company = Company::query()->create([
'name' => 'Kontakt GmbH',
'slug' => 'kontakt-gmbh',
'portal' => 'businessportal24',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.contacts.create')
->set('companyId', $company->id)
->set('portal', 'businessportal24')
->set('salutationKey', 'mr')
->set('firstName', 'Paul')
->set('lastName', 'Neumann')
->set('responsibility', 'Pressekontakt')
->set('email', 'paul@kontakt-gmbh.de')
->set('phone', '040123456')
->call('save')
->assertHasNoErrors();
$contact = Contact::query()->where('email', 'paul@kontakt-gmbh.de')->firstOrFail();
expect($contact->company_id)->toBe($company->id);
expect($contact->first_name)->toBe('Paul');
LivewireVolt::test('admin.contacts.edit', ['id' => $contact->id])
->assertSee('Businessportal24')
->set('firstName', 'Paula')
->set('responsibility', 'Leitung PR')
->call('save')
->assertHasNoErrors();
$contact->refresh();
expect($contact->first_name)->toBe('Paula');
expect($contact->responsibility)->toBe('Leitung PR');
LivewireVolt::test('admin.contacts.edit', ['id' => $contact->id])
->call('deleteContact')
->assertRedirect(route('admin.contacts.index'));
expect(Contact::query()->whereKey($contact->id)->exists())->toBeFalse();
expect(Contact::query()->withTrashed()->find($contact->id)?->trashed())->toBeTrue();
});
test('company contact create route prefills company in contact form', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$company = Company::query()->create([
'name' => 'Prefill GmbH',
'slug' => 'prefill-gmbh',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.contacts.create', ['companyId' => $company->id])
->assertSet('companyId', $company->id)
->assertSet('isCompanyPrefilled', true);
});
test('admin can filter contacts in company detail contacts tab', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$company = Company::query()->create([
'name' => 'Filter GmbH',
'slug' => 'filter-gmbh',
'portal' => 'businessportal24',
]);
Contact::query()->create([
'company_id' => $company->id,
'portal' => 'businessportal24',
'first_name' => 'Anna',
'last_name' => 'Presse',
'email' => 'anna@filter-gmbh.de',
]);
Contact::query()->create([
'company_id' => $company->id,
'first_name' => 'Bernd',
'last_name' => 'Marketing',
'email' => 'bernd@filter-gmbh.de',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.companies.show', ['id' => $company->id])
->assertSee('Businessportal24')
->set('activeTab', 'contacts')
->set('contactSearch', 'anna')
->assertSee('Anna Presse')
->assertSee('Businessportal24')
->assertDontSee('Bernd Marketing');
});
test('admin can live-search and assign existing contacts in company detail with result limit', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$targetCompany = Company::query()->create([
'name' => 'Target GmbH',
'slug' => 'target-gmbh',
]);
$sourceCompany = Company::query()->create([
'name' => 'Source GmbH',
'slug' => 'source-gmbh',
]);
Contact::query()->create([
'company_id' => $targetCompany->id,
'first_name' => 'Existing',
'last_name' => 'Target',
'email' => 'existing@target.test',
]);
$movableContact = Contact::query()->create([
'company_id' => $sourceCompany->id,
'first_name' => 'Zora',
'last_name' => 'Finder',
'email' => 'zora@source.test',
]);
for ($i = 1; $i <= 45; $i++) {
Contact::query()->create([
'company_id' => $sourceCompany->id,
'first_name' => 'Limit',
'last_name' => sprintf('Result%02d', $i),
'email' => 'limit'.$i.'@source.test',
]);
}
$this->actingAs($admin);
LivewireVolt::test('admin.companies.show', ['id' => $targetCompany->id])
->set('activeTab', 'contacts')
->set('contactLookup', 'Limit')
->assertViewHas('contactLookupResults', fn ($results) => $results->count() === 45) // 45 Treffer, Limit 50
->set('contactLookup', 'Zora')
->set('selectedExistingContactId', $movableContact->id)
->call('attachExistingContact')
->assertHasNoErrors();
$movableContact->refresh();
expect($movableContact->company_id)->toBe($targetCompany->id);
});
test('admin can filter contacts index by portal and see company quick-jump link', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$presseechoCompany = Company::query()->create([
'name' => 'Presseecho Firma',
'slug' => 'presseecho-firma',
'portal' => 'presseecho',
]);
$bpCompany = Company::query()->create([
'name' => 'BP Firma',
'slug' => 'bp-firma',
'portal' => 'businessportal24',
]);
Contact::query()->create([
'company_id' => $presseechoCompany->id,
'portal' => 'presseecho',
'first_name' => 'Paula',
'last_name' => 'Echo',
'email' => 'paula@echo.de',
]);
Contact::query()->create([
'company_id' => $bpCompany->id,
'portal' => 'businessportal24',
'first_name' => 'Boris',
'last_name' => 'Portal',
'email' => 'boris@bp.de',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.contacts.index')
->assertSee('Portal')
->set('portalFilter', 'presseecho')
->assertSee('Paula Echo')
->assertSee('Presseecho')
->assertDontSee('Boris Portal')
->assertSee('/admin/companies/'.$presseechoCompany->id, false);
});
test('admin contacts index shows press release counts and supports entity filters', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$targetUser = User::factory()->create([
'name' => 'Kontakt Filter User',
'email' => 'kontakt-filter-user@example.com',
]);
$companyWithPressRelease = Company::query()->create([
'name' => 'Kontakt PM Firma',
'slug' => 'kontakt-pm-firma',
]);
$companyWithoutPressRelease = Company::query()->create([
'name' => 'Kontakt Ohne PM Firma',
'slug' => 'kontakt-ohne-pm-firma',
]);
$contactWithPressRelease = Contact::query()->create([
'company_id' => $companyWithPressRelease->id,
'first_name' => 'Klara',
'last_name' => 'Pressekontakt',
'email' => 'klara@kontakt-pm.test',
]);
$contactWithoutPressRelease = Contact::query()->create([
'company_id' => $companyWithoutPressRelease->id,
'first_name' => 'Olaf',
'last_name' => 'Ohnepm',
'email' => 'olaf@kontakt-ohne-pm.test',
]);
$contactWithPressRelease->users()->attach($targetUser->id);
$pressRelease = PressRelease::factory()
->for($targetUser)
->for($companyWithPressRelease)
->published()
->create(['title' => 'Kontakt PM Zaehler']);
$pressRelease->contacts()->attach($contactWithPressRelease->id);
$this->actingAs($admin);
LivewireVolt::test('admin.contacts.index')
->assertSee('Klara')
->assertSee('1 PMs')
->assertSee(route('admin.press-releases.index', ['contact' => $contactWithPressRelease->id]), false)
->assertSee('Alle Datenstände')
->set('qualityFilter', 'with_press_releases')
->assertSee('Klara')
->assertDontSee('Olaf')
->set('qualityFilter', 'without_press_releases')
->assertSee('Olaf')
->assertDontSee('Klara')
->set('qualityFilter', 'all')
->set('userSearch', 'kontakt-filter-user')
->assertViewHas('filterUsers', fn ($results) => $results->pluck('id')->contains($targetUser->id))
->set('userFilter', (string) $targetUser->id)
->assertSee('Klara')
->assertDontSee('Olaf')
->call('clearUserSearch')
->assertSet('userFilter', 'all')
->assertSet('userSearch', '')
->set('companySearch', 'Kontakt PM')
->assertViewHas('filterCompanies', fn ($results) => $results->pluck('id')->contains($companyWithPressRelease->id))
->set('companyFilter', (string) $companyWithPressRelease->id)
->assertSee('Klara')
->call('clearCompanySearch')
->assertSet('companyFilter', 'all')
->assertSet('companySearch', '');
});
test('admin can soft delete contact directly from contacts index flow', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$company = Company::query()->create([
'name' => 'Index Delete GmbH',
'slug' => 'index-delete-gmbh',
]);
$contact = Contact::query()->create([
'company_id' => $company->id,
'portal' => 'both',
'first_name' => 'Delete',
'last_name' => 'Me',
'email' => 'delete-me@index-contact.test',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.contacts.index')
->call('deleteContactFromIndex', $contact->id)
->assertHasNoErrors();
expect(Contact::query()->whereKey($contact->id)->exists())->toBeFalse();
expect(Contact::query()->withTrashed()->find($contact->id)?->trashed())->toBeTrue();
});
test('admin companies index shows relation counts and supports entity filters', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
Storage::fake('public');
$admin = User::factory()->create();
$admin->assignRole('admin');
$targetUser = User::factory()->create([
'name' => 'Firmen Filter User',
'email' => 'firmen-filter-user@example.com',
]);
$companyWithData = Company::query()->create([
'name' => 'Firmen PM Zaehler GmbH',
'slug' => 'firmen-pm-zaehler',
'email' => 'pm@firma.test',
'portal' => 'businessportal24',
'legacy_portal' => 'businessportal24',
'logo_path' => 'legacy-logo.png',
'is_active' => true,
]);
$companyWithoutData = Company::query()->create([
'name' => 'Firmen Leer GmbH',
'slug' => 'firmen-leer',
'email' => 'leer@firma.test',
'portal' => 'presseecho',
'is_active' => true,
]);
Storage::disk('public')->put('businessportal24/company/legacy-logo.png', 'logo');
$companyWithData->users()->attach($targetUser->id);
$contact = Contact::query()->create([
'company_id' => $companyWithData->id,
'first_name' => 'Carla',
'last_name' => 'Firmenkontakt',
'email' => 'carla@firma.test',
]);
PressRelease::factory()
->count(2)
->for($targetUser)
->for($companyWithData)
->published()
->create();
$this->actingAs($admin);
$legacyLogoUrl = asset('storage/businessportal24/company/legacy-logo.png');
LivewireVolt::test('admin.companies.index')
->assertSee('Portal')
->assertSee('Alle Portale')
->assertSee('Firmen PM Zaehler GmbH')
->assertSee('Businessportal24')
->assertSee($legacyLogoUrl, false)
->assertSee('2 PMs')
->assertSee('1 Kontakte')
->assertSee(route('admin.press-releases.index', ['company' => $companyWithData->id]), false)
->assertSee(route('admin.contacts.index', ['company' => $companyWithData->id]), false)
->set('portalFilter', 'businessportal24')
->assertSee('Firmen PM Zaehler GmbH')
->assertDontSee('Firmen Leer GmbH')
->set('portalFilter', 'all')
->set('qualityFilter', 'with_press_releases')
->assertSee('Firmen PM Zaehler GmbH')
->assertDontSee('Firmen Leer GmbH')
->set('qualityFilter', 'without_press_releases')
->assertSee('Firmen Leer GmbH')
->assertDontSee('Firmen PM Zaehler GmbH')
->set('qualityFilter', 'all')
->set('userLookup', 'firmen-filter-user')
->assertViewHas('userLookupResults', fn ($results) => $results->pluck('id')->contains($targetUser->id))
->set('userFilter', (string) $targetUser->id)
->assertSee('Firmen PM Zaehler GmbH')
->assertDontSee('Firmen Leer GmbH')
->call('clearUserSearch')
->assertSet('userFilter', 'all')
->assertSet('userLookup', '')
->set('contactLookup', 'Carla')
->assertViewHas('contactLookupResults', fn ($results) => $results->pluck('id')->contains($contact->id))
->set('contactFilter', (string) $contact->id)
->assertSee('Firmen PM Zaehler GmbH')
->call('clearContactSearch')
->assertSet('contactFilter', 'all')
->assertSet('contactLookup', '');
LivewireVolt::test('admin.companies.show', ['id' => $companyWithData->id])
->assertSee($legacyLogoUrl, false);
LivewireVolt::test('admin.companies.edit', ['id' => $companyWithData->id])
->assertSet('current_logo_url', $legacyLogoUrl);
});
test('admin can save apply and delete contact filter presets', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$presseechoCompany = Company::query()->create([
'name' => 'Preset Presseecho',
'slug' => 'preset-presseecho',
'portal' => 'presseecho',
]);
Contact::query()->create([
'company_id' => $presseechoCompany->id,
'portal' => 'presseecho',
'first_name' => 'Preset',
'last_name' => 'Kontakt',
'email' => 'preset@example.de',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.contacts.index')
->set('search', 'preset')
->set('companyFilter', (string) $presseechoCompany->id)
->set('portalFilter', 'presseecho')
->set('presetName', 'Nur Presseecho Kontakte')
->call('savePreset')
->assertHasNoErrors();
$preset = UserFilterPreset::query()
->where('user_id', $admin->id)
->where('page', 'admin.contacts.index')
->where('name', 'Nur Presseecho Kontakte')
->firstOrFail();
LivewireVolt::test('admin.contacts.index')
->set('selectedPresetId', $preset->id)
->call('applyPreset')
->assertSet('search', 'preset')
->assertSet('companyFilter', (string) $presseechoCompany->id)
->assertSet('portalFilter', 'presseecho')
->call('deletePreset');
expect(UserFilterPreset::query()->whereKey($preset->id)->exists())->toBeFalse();
});
test('admin can set default preset and it is auto-applied on contacts index load', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$presseechoCompany = Company::query()->create([
'name' => 'Default Preset Firma',
'slug' => 'default-preset-firma',
'portal' => 'presseecho',
]);
$this->actingAs($admin);
LivewireVolt::test('admin.contacts.index')
->set('search', 'default')
->set('companyFilter', (string) $presseechoCompany->id)
->set('portalFilter', 'presseecho')
->set('presetName', 'Default Kontakte')
->call('savePreset')
->assertHasNoErrors();
$preset = UserFilterPreset::query()
->where('user_id', $admin->id)
->where('page', 'admin.contacts.index')
->where('name', 'Default Kontakte')
->firstOrFail();
LivewireVolt::test('admin.contacts.index')
->set('selectedPresetId', $preset->id)
->call('setDefaultPreset')
->assertHasNoErrors();
$preset->refresh();
expect($preset->is_default)->toBeTrue();
LivewireVolt::test('admin.contacts.index')
->assertSet('selectedPresetId', $preset->id)
->assertSet('search', 'default')
->assertSet('companyFilter', (string) $presseechoCompany->id)
->assertSet('portalFilter', 'presseecho');
});
test('applying a preset updates last used timestamp and affects ordering', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$firstPreset = UserFilterPreset::query()->create([
'user_id' => $admin->id,
'page' => 'admin.contacts.index',
'name' => 'Aelteres Preset',
'filters' => [
'search' => '',
'company_filter' => 'all',
'portal_filter' => 'all',
],
'last_used_at' => now()->subDay(),
]);
$secondPreset = UserFilterPreset::query()->create([
'user_id' => $admin->id,
'page' => 'admin.contacts.index',
'name' => 'Neueres Preset',
'filters' => [
'search' => '',
'company_filter' => 'all',
'portal_filter' => 'all',
],
'last_used_at' => now()->subHours(2),
]);
$this->actingAs($admin);
LivewireVolt::test('admin.contacts.index')
->set('selectedPresetId', $firstPreset->id)
->call('applyPreset')
->assertHasNoErrors();
$firstPreset->refresh();
expect($firstPreset->last_used_at)->not->toBeNull();
expect($firstPreset->last_used_at?->greaterThan($secondPreset->last_used_at))->toBeTrue();
});
test('admin can create and edit company with persisted data', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$this->actingAs($admin);
LivewireVolt::test('admin.companies.create')
->set('portal', 'presseecho')
->set('type', 'company')
->set('company_name', 'Persist GmbH')
->set('email', 'info@persist-gmbh.de')
->set('phone', '04099999')
->set('website', 'https://persist-gmbh.de')
->set('street', 'Testweg 12')
->set('zip', '20095')
->set('city', 'Hamburg')
->set('country', 'DE')
->set('is_active', true)
->call('save')
->assertHasNoErrors();
$company = Company::query()->where('name', 'Persist GmbH')->firstOrFail();
expect($company->portal?->value)->toBe('presseecho');
expect($company->slug)->toBe('persist-gmbh');
expect($company->email)->toBe('info@persist-gmbh.de');
expect($company->address)->toContain('Testweg 12');
LivewireVolt::test('admin.companies.edit', ['id' => $company->id])
->set('company_name', 'Persist GmbH Updated')
->set('portal', 'businessportal24')
->set('type', 'agency')
->set('email', 'office@persist-gmbh.de')
->set('phone', '04077777')
->set('website', 'https://updated-persist.de')
->set('street', 'Neuer Weg 1')
->set('zip', '50667')
->set('city', 'Koeln')
->set('country', 'DE')
->set('is_active', false)
->call('update')
->assertHasNoErrors();
$company->refresh();
expect($company->name)->toBe('Persist GmbH Updated');
expect($company->portal?->value)->toBe('businessportal24');
expect($company->type?->value)->toBe('agency');
expect($company->email)->toBe('office@persist-gmbh.de');
expect($company->is_active)->toBeFalse();
});
test('admin can soft delete company from edit flow', function () {
/** @var TestCase $this */
$this->seed(RolesAndPermissionsSeeder::class);
$admin = User::factory()->create();
$admin->assignRole('admin');
$company = Company::query()->create([
'portal' => 'both',
'type' => 'company',
'name' => 'Delete Me GmbH',
'slug' => 'delete-me-gmbh',
'email' => 'delete@company.test',
'is_active' => true,
]);
$this->actingAs($admin);
LivewireVolt::test('admin.companies.edit', ['id' => $company->id])
->call('deleteCompany')
->assertRedirect(route('admin.companies.index'));
expect(Company::query()->whereKey($company->id)->exists())->toBeFalse();
expect(Company::query()->withTrashed()->find($company->id)?->trashed())->toBeTrue();
});