seed(RolesAndPermissionsSeeder::class); }); function makeCustomerWithPressRelease(array $prAttributes = []): array { $customer = User::factory()->create(['is_active' => true]); $customer->assignRole('customer'); $company = Company::factory()->presseecho()->create(); $customer->companies()->attach($company->id, ['role' => 'owner']); $contact = Contact::factory()->for($company)->create([ 'portal' => $company->portal->value, ]); $category = Category::factory()->create(); $pr = PressRelease::factory()->create(array_merge([ 'user_id' => $customer->id, 'company_id' => $company->id, 'category_id' => $category->id, 'portal' => $company->portal->value, 'status' => 'draft', ], $prAttributes)); $pr->contacts()->sync([$contact->id]); return compact('customer', 'company', 'contact', 'category', 'pr'); } test('editor shows the content score panel when a score exists', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease([ 'content_score' => 67, 'content_tier' => PressReleaseContentTier::Geprueft->value, ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertSet('contentScore', 67) ->assertSee('67') ->assertSee('Geprüft') ->assertSee('Noch 13 Punkte bis zur nächsten Stufe.'); }); test('mount converts legacy plain-text body to editable html so line breaks survive', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease([ 'text' => "Erster Absatz mit Inhalt.\nZweite Zeile desselben Absatzes.\n\nNeuer Absatz nach Leerzeile.", ]); $this->actingAs($customer); $component = Livewire::test('customer.press-releases.edit', ['id' => $pr->id]); $loaded = $component->get('text'); expect($loaded)->toContain('
');
expect($loaded)->toContain('Erster Absatz mit Inhalt.
toContain('
Neuer Absatz nach Leerzeile.
'); }); test('mount leaves stored html body untouched in structure', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease([ 'text' => 'Absatz eins.
Absatz zwei.
', ]); $this->actingAs($customer); $loaded = Livewire::test('customer.press-releases.edit', ['id' => $pr->id])->get('text'); expect($loaded)->toContain('Absatz eins.
'); expect($loaded)->toContain('Absatz zwei.
'); }); test('mount loads all Phase 7 fields and pivot contact', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr, 'contact' => $contact] = makeCustomerWithPressRelease([ 'subtitle' => 'Untertitel der PM', 'boilerplate_override' => 'Spezielle Boilerplate.', 'keywords' => 'Test, Brauerei', ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertSet('subtitle', 'Untertitel der PM') ->assertSet('boilerplateOverride', 'Spezielle Boilerplate.') ->assertSet('useBoilerplateOverride', true) ->assertSet('keywords', 'Test, Brauerei') ->assertSet('contactId', $contact->id) ->assertSet('currentStatus', 'draft'); }); test('mount falls back to first company contact when no pivot exists', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr, 'contact' => $contact] = makeCustomerWithPressRelease(); $pr->contacts()->detach(); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertSet('contactId', $contact->id); }); test('edit page shows uploaded title image instead of placeholder controls', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease([ 'title' => 'PM mit eigenem Titelbild', ]); $pr->images()->create([ 'disk' => 'public', 'path' => 'press-releases/'.$pr->id.'/images/title.jpg', 'variants' => ['cover' => 'press-releases/'.$pr->id.'/images/title-cover.jpg'], 'author' => 'Jane Doe', 'license_type' => ImageLicenseType::Own->value, 'rights_confirmed_at' => now(), 'is_preview' => true, 'sort_order' => 1, ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertDontSee('Dieser Platzhalter wird angezeigt, bis ein eigenes Titelbild hochgeladen ist.') ->assertDontSee('Platzhalter wählen'); }); test('save persists all new Phase 7 fields and syncs contact', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease(); $newContact = Contact::factory()->for($pr->company)->create([ 'portal' => $pr->company->portal->value, ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->set('title', 'Aktualisierter Titel mit genug Zeichen') ->set('subtitle', 'Neue Subline') ->set('text', str_repeat('Aktualisierter Fließtext mit etwas Inhalt. ', 5)) ->set('keywords', 'Neu, Frisch') ->set('contactId', $newContact->id) ->set('useBoilerplateOverride', true) ->set('boilerplateOverride', 'Override-Text für diese PM.') ->call('save') ->assertHasNoErrors(); $pr->refresh(); expect($pr->title)->toBe('Aktualisierter Titel mit genug Zeichen'); expect($pr->subtitle)->toBe('Neue Subline'); expect($pr->boilerplate_override)->toBe('Override-Text für diese PM.'); expect($pr->keywords)->toBe('Neu, Frisch'); expect($pr->contacts->pluck('id')->all())->toBe([$newContact->id]); }); test('save without contact id succeeds and detaches existing contact', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease(); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->set('contactId', null) ->call('save') ->assertHasNoErrors(['contactId']); expect($pr->fresh()->contacts()->count())->toBe(0); }); test('save nulls boilerplate_override when toggle is turned off', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease([ 'boilerplate_override' => 'Alter Override-Text', ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertSet('useBoilerplateOverride', true) ->set('useBoilerplateOverride', false) ->call('save') ->assertHasNoErrors(); $pr->refresh(); expect($pr->boilerplate_override)->toBeNull(); }); test('changing the company resets the contact to the new company default', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease(); $secondCompany = Company::factory()->presseecho()->create(); $customer->companies()->attach($secondCompany->id, ['role' => 'owner']); $secondContact = Contact::factory()->for($secondCompany)->create([ 'portal' => $secondCompany->portal->value, ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->set('companyId', $secondCompany->id) ->assertSet('contactId', $secondContact->id); }); test('company options keep selected company and search without loading all companies', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr, 'company' => $selectedCompany] = makeCustomerWithPressRelease(); $companies = Company::factory() ->count(12) ->presseecho() ->sequence(fn (Sequence $sequence): array => [ 'name' => sprintf('Weitere Firma %02d', $sequence->index + 1), ]) ->create(); $companies->each(fn (Company $company) => $customer->companies()->attach($company->id, ['role' => 'owner'])); $targetCompany = $companies->first(); $targetCompany->update(['name' => 'Suchtreffer Redaktion']); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertViewHas('companyOptions', fn ($companyOptions): bool => $companyOptions->count() === 10 && $companyOptions->pluck('id')->contains($selectedCompany->id)) ->set('companySearch', 'Suchtreffer') ->assertViewHas('companyOptions', fn ($companyOptions): bool => $companyOptions->count() === 1 && $companyOptions->first()->id === $targetCompany->id); }); test('rejected press releases can be edited and re-submitted', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease([ 'status' => 'rejected', ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertSet('currentStatus', 'rejected'); }); test('foreign press release returns 403 on mount', function () { /** @var TestCase $this */ ['pr' => $pr] = makeCustomerWithPressRelease(); $stranger = User::factory()->create(['is_active' => true]); $stranger->assignRole('customer'); $this->actingAs($stranger); $this->get(route('me.press-releases.edit', $pr->id)) ->assertNotFound(); }); test('editor shows the rejection reason for a rejected press release', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease([ 'status' => 'rejected', ]); PressReleaseStatusLog::query()->create([ 'press_release_id' => $pr->id, 'from_status' => 'review', 'to_status' => 'rejected', 'reason' => 'Werbliche Sprache, bitte überarbeiten.', 'source' => 'admin', 'created_at' => now(), ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertSee('Diese Pressemitteilung wurde abgelehnt') ->assertSee('Werbliche Sprache, bitte überarbeiten.') ->assertSee('Abgelehnt am'); }); test('editor shows no rejection panel for drafts', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease(); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->assertDontSee('Diese Pressemitteilung wurde abgelehnt'); }); test('addTag and removeTag work in edit form', function () { /** @var TestCase $this */ ['customer' => $customer, 'pr' => $pr] = makeCustomerWithPressRelease([ 'keywords' => 'Eins', ]); $this->actingAs($customer); Livewire::test('customer.press-releases.edit', ['id' => $pr->id]) ->call('addTag', 'Zwei') ->assertSet('keywords', 'Eins, Zwei') ->call('removeTag', 'Eins') ->assertSet('keywords', 'Zwei'); });