- Add email branding columns to organisations table (logo, color, reply-to, sender name, footer)
- Create MailBrandingService for resolving per-org branding with defaults
- Create CrewliMailable abstract base class with branded from/reply-to
- Create shared Blade layout (mail.layouts.crewli) with inline CSS
- Refactor Registration*Mail and InvitationMail to extend CrewliMailable
- Add config/crewli.php for platform-wide defaults (portal_url, app_url, logo)
- Add dev-only /mail-preview/{type} route for browser email previewing
- Update Organisation model, resource, and form requests with branding fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
158 lines
5.3 KiB
PHP
158 lines
5.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Mail;
|
|
|
|
use App\Mail\CrewliMailable;
|
|
use App\Mail\InvitationMail;
|
|
use App\Mail\RegistrationApprovedMail;
|
|
use App\Mail\RegistrationConfirmationMail;
|
|
use App\Mail\RegistrationRejectedMail;
|
|
use App\Models\CrowdType;
|
|
use App\Models\Event;
|
|
use App\Models\Organisation;
|
|
use App\Models\Person;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class MailLayoutTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private Organisation $organisation;
|
|
private Event $event;
|
|
private Person $person;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->organisation = Organisation::factory()->create([
|
|
'name' => 'Test Festival BV',
|
|
'email_logo_url' => 'https://example.com/logo.png',
|
|
'email_primary_color' => '#e11d48',
|
|
'email_footer_text' => 'Stichting Test Festival, Utrecht',
|
|
]);
|
|
|
|
$this->event = Event::factory()->create([
|
|
'organisation_id' => $this->organisation->id,
|
|
'status' => 'registration_open',
|
|
]);
|
|
|
|
$crowdType = CrowdType::factory()->systemType('VOLUNTEER')->create([
|
|
'organisation_id' => $this->organisation->id,
|
|
]);
|
|
|
|
$this->person = Person::factory()->create([
|
|
'event_id' => $this->event->id,
|
|
'crowd_type_id' => $crowdType->id,
|
|
'first_name' => 'Jan',
|
|
'last_name' => 'Tester',
|
|
'email' => 'jan@test.nl',
|
|
]);
|
|
}
|
|
|
|
public function test_confirmation_email_renders_with_org_logo(): void
|
|
{
|
|
$mail = new RegistrationConfirmationMail($this->person, $this->event);
|
|
$html = $mail->render();
|
|
|
|
$this->assertStringContainsString('https://example.com/logo.png', $html);
|
|
$this->assertStringContainsString('Test Festival BV', $html);
|
|
}
|
|
|
|
public function test_confirmation_email_renders_with_primary_color(): void
|
|
{
|
|
$mail = new RegistrationConfirmationMail($this->person, $this->event);
|
|
$html = $mail->render();
|
|
|
|
$this->assertStringContainsString('#e11d48', $html);
|
|
}
|
|
|
|
public function test_confirmation_email_renders_with_custom_footer(): void
|
|
{
|
|
$mail = new RegistrationConfirmationMail($this->person, $this->event);
|
|
$html = $mail->render();
|
|
|
|
$this->assertStringContainsString('Stichting Test Festival, Utrecht', $html);
|
|
}
|
|
|
|
public function test_confirmation_email_renders_with_defaults_when_no_branding(): void
|
|
{
|
|
$plainOrg = Organisation::factory()->create([
|
|
'name' => 'Plain Org',
|
|
'email_logo_url' => null,
|
|
'email_primary_color' => null,
|
|
'email_footer_text' => null,
|
|
]);
|
|
|
|
$event = Event::factory()->create([
|
|
'organisation_id' => $plainOrg->id,
|
|
'status' => 'registration_open',
|
|
]);
|
|
|
|
$crowdType = CrowdType::factory()->systemType('VOLUNTEER')->create([
|
|
'organisation_id' => $plainOrg->id,
|
|
]);
|
|
|
|
$person = Person::factory()->create([
|
|
'event_id' => $event->id,
|
|
'crowd_type_id' => $crowdType->id,
|
|
]);
|
|
|
|
$mail = new RegistrationConfirmationMail($person, $event);
|
|
$html = $mail->render();
|
|
|
|
// Default primary color
|
|
$this->assertStringContainsString('#6366f1', $html);
|
|
// Org name is shown
|
|
$this->assertStringContainsString('Plain Org', $html);
|
|
// Powered by Crewli always present
|
|
$this->assertStringContainsString('Powered by Crewli', $html);
|
|
}
|
|
|
|
public function test_approved_email_renders_with_branding(): void
|
|
{
|
|
$mail = new RegistrationApprovedMail($this->person, $this->event);
|
|
$html = $mail->render();
|
|
|
|
$this->assertStringContainsString('https://example.com/logo.png', $html);
|
|
$this->assertStringContainsString('#e11d48', $html);
|
|
$this->assertStringContainsString('Goed nieuws', $html);
|
|
}
|
|
|
|
public function test_rejected_email_renders_with_reason(): void
|
|
{
|
|
$mail = new RegistrationRejectedMail($this->person, $this->event, 'Geen plek meer.');
|
|
$html = $mail->render();
|
|
|
|
$this->assertStringContainsString('Geen plek meer.', $html);
|
|
$this->assertStringContainsString('Test Festival BV', $html);
|
|
}
|
|
|
|
public function test_rejected_email_renders_without_reason(): void
|
|
{
|
|
$mail = new RegistrationRejectedMail($this->person, $this->event);
|
|
$html = $mail->render();
|
|
|
|
$this->assertStringNotContainsString('Reden:', $html);
|
|
}
|
|
|
|
public function test_all_mailables_extend_crewli_mailable(): void
|
|
{
|
|
$this->assertInstanceOf(CrewliMailable::class, new RegistrationConfirmationMail($this->person, $this->event));
|
|
$this->assertInstanceOf(CrewliMailable::class, new RegistrationApprovedMail($this->person, $this->event));
|
|
$this->assertInstanceOf(CrewliMailable::class, new RegistrationRejectedMail($this->person, $this->event));
|
|
}
|
|
|
|
public function test_powered_by_crewli_always_present(): void
|
|
{
|
|
$mail = new RegistrationConfirmationMail($this->person, $this->event);
|
|
$html = $mail->render();
|
|
|
|
$this->assertStringContainsString('Powered by Crewli', $html);
|
|
$this->assertStringContainsString('Je ontvangt deze email omdat je bent aangemeld bij', $html);
|
|
}
|
|
}
|