Answer the inbox from the panel, with templates
A reply starts from a template filled in the language the person wrote in, is sent by the application when it has a mailer that delivers or handed to the replier's mail program otherwise, and is recorded on the submission. The same is available through the API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
139
tests/Feature/Panel/InboxRepliesTest.php
Normal file
139
tests/Feature/Panel/InboxRepliesTest.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Panel;
|
||||
|
||||
use App\Enums\ApplicationStatus;
|
||||
use App\Filament\Resources\Applications\Pages\ViewApplication;
|
||||
use App\Filament\Resources\ReplyTemplates\Pages\CreateReplyTemplate;
|
||||
use App\Filament\Resources\ReplyTemplates\ReplyTemplateResource;
|
||||
use App\Mail\ApplicationReplyMail;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationReply;
|
||||
use App\Models\ReplyTemplate;
|
||||
use Database\Seeders\RolePermissionSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Livewire\Livewire;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use Tests\FakesPireon;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Answering the inbox from the panel and the API, with templates.
|
||||
*/
|
||||
class InboxRepliesTest extends TestCase
|
||||
{
|
||||
use FakesPireon, RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->seed(RolePermissionSeeder::class);
|
||||
Mail::fake();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_template_is_filled_in_the_language_they_wrote_in(): void
|
||||
{
|
||||
$template = ReplyTemplate::factory()->create();
|
||||
$application = Application::factory()->create(['name' => 'Ada', 'locale' => 'en']);
|
||||
|
||||
$text = $template->renderFor($application, $this->userWithRoles('editor', ['name' => 'Linus']));
|
||||
|
||||
$this->assertSame('Thank you, Ada', $text['subject']);
|
||||
$this->assertStringContainsString('Linus', $text['body']);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function with_a_real_mailer_the_reply_is_sent_and_recorded(): void
|
||||
{
|
||||
config(['mail.default' => 'smtp']);
|
||||
|
||||
$this->actingAs($editor = $this->userWithRoles('editor'));
|
||||
|
||||
$template = ReplyTemplate::factory()->create();
|
||||
$application = Application::factory()->create(['name' => 'Ada', 'locale' => 'es', 'email' => 'ada@example.org']);
|
||||
|
||||
Livewire::test(ViewApplication::class, ['record' => $application->getRouteKey()])
|
||||
->callAction('reply', ['template_id' => $template->id, 'subject' => 'Gracias, Ada', 'body' => 'Hola Ada'])
|
||||
->assertHasNoActionErrors();
|
||||
|
||||
Mail::assertSent(ApplicationReplyMail::class, fn (ApplicationReplyMail $mail) => $mail->hasTo('ada@example.org'));
|
||||
|
||||
$reply = $application->replies()->sole();
|
||||
|
||||
$this->assertSame(ApplicationReply::MAIL, $reply->channel);
|
||||
$this->assertSame($editor->id, $reply->user_id);
|
||||
$this->assertSame(ApplicationStatus::Contacted, $application->fresh()->status);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function without_one_it_is_recorded_and_handed_to_the_mail_program(): void
|
||||
{
|
||||
config(['mail.default' => 'log']);
|
||||
|
||||
$this->actingAs($this->userWithRoles('editor'));
|
||||
|
||||
$application = Application::factory()->create();
|
||||
|
||||
Livewire::test(ViewApplication::class, ['record' => $application->getRouteKey()])
|
||||
->callAction('reply', ['subject' => 'Re', 'body' => 'Gracias'])
|
||||
->assertHasNoActionErrors();
|
||||
|
||||
Mail::assertNothingSent();
|
||||
$this->assertSame(ApplicationReply::MAILTO, $application->replies()->sole()->channel);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function a_decision_already_taken_is_not_undone_by_replying(): void
|
||||
{
|
||||
config(['mail.default' => 'smtp']);
|
||||
|
||||
$this->actingAs($this->userWithRoles('editor'));
|
||||
|
||||
$application = Application::factory()->create(['status' => ApplicationStatus::Accepted]);
|
||||
|
||||
Livewire::test(ViewApplication::class, ['record' => $application->getRouteKey()])
|
||||
->callAction('reply', ['subject' => 'Bienvenida', 'body' => '¡Adelante!']);
|
||||
|
||||
$this->assertSame(ApplicationStatus::Accepted, $application->fresh()->status);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function templates_are_written_by_whoever_answers_the_inbox(): void
|
||||
{
|
||||
$this->actingAs($this->userWithRoles('editor'));
|
||||
|
||||
Livewire::test(CreateReplyTemplate::class)
|
||||
->fillForm(['name' => 'Gracias', 'subject' => ['es' => 'Gracias'], 'body' => ['es' => 'Hola {name}']])
|
||||
->call('create')
|
||||
->assertHasNoFormErrors();
|
||||
|
||||
$this->assertSame(1, ReplyTemplate::query()->count());
|
||||
|
||||
$this->actingAs($this->userWithRoles('writer'));
|
||||
$this->get(ReplyTemplateResource::getUrl('index'))->assertForbidden();
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function the_api_replies_from_a_template_and_lists_replies(): void
|
||||
{
|
||||
config(['mail.default' => 'log']);
|
||||
|
||||
$this->withHeaders($this->apiHeadersFor($this->userWithRoles('editor')));
|
||||
|
||||
$template = ReplyTemplate::factory()->create();
|
||||
$application = Application::factory()->create(['name' => 'Ada', 'locale' => 'ca']);
|
||||
|
||||
$this->postJson("/api/v1/applications/{$application->id}/reply", ['template_id' => $template->id])
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.subject', 'Gràcies, Ada')
|
||||
->assertJsonPath('data.channel', 'mailto');
|
||||
|
||||
$this->getJson("/api/v1/applications/{$application->id}/replies")->assertJsonCount(1, 'data');
|
||||
$this->getJson('/api/v1/reply-templates')->assertJsonPath('data.0.subject.en', 'Thank you, {name}');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user